音频播放系统.md 25 KB

音频播放系统

本文档引用的文件

  • player.controller.ts
  • player.service.ts
  • audio.ts
  • index.vue
  • MiniPlayer.vue
  • index.ts
  • websocket.service.ts
  • audio-merger.ts
  • schema.prisma
  • 20260422105352_add_content_status/migration.sql

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构概览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排除指南
  9. 结论
  10. 附录

简介

音频播放系统是一个基于UniApp框架构建的移动端音频播放解决方案,支持TTS文本转语音、章节音频播放、播放列表管理、进度记录与恢复等功能。系统采用前后端分离架构,前端使用Vue3 + Pinia状态管理,后端使用Koa框架和Prisma ORM。

该系统主要面向AI有声书场景,提供完整的音频播放体验,包括播放控制、列表管理、进度跟踪、后台播放支持等核心功能。

项目结构

音频播放系统的项目结构采用模块化组织方式:

graph TB
subgraph "前端 (my-uniapp-vue3)"
A[src/store/audio.ts - 音频状态管理]
B[src/pages/player/index.vue - 播放器页面]
C[src/components/MiniPlayer.vue - 迷你播放器]
D[src/types/index.ts - 类型定义]
end
subgraph "后端 (server)"
E[src/modules/player/player.controller.ts - 播放器控制器]
F[src/modules/player/player.service.ts - 播放器服务]
G[src/services/websocket.service.ts - WebSocket服务]
H[src/modules/tts/audio-merger.ts - 音频合并器]
I[prisma/schema.prisma - 数据库模型]
end
subgraph "数据库"
J[PlayRecord - 播放记录表]
K[BookChapter - 章节表]
L[User - 用户表]
end
A --> B
B --> E
E --> F
F --> I
G --> B
H --> F
I --> J
I --> K
I --> L

图表来源

  • audio.ts:1-297
  • index.vue:1-800
  • player.controller.ts:1-344

章节来源

  • audio.ts:1-297
  • player.controller.ts:1-344

核心组件

音频状态管理 (Pinia Store)

音频状态管理是整个播放系统的核心,负责维护播放器的各种状态和行为:

  • 播放状态管理: isPlaying、currentTime、duration、playRate
  • 播放列表管理: playlist、currentIndex、hasPlaylist、hasNext、hasPrev
  • 播放模式控制: sequence、loop、single、random四种模式
  • 音频上下文管理: 通过uni.createInnerAudioContext创建和管理音频实例

播放器控制器 (Koa Router)

后端播放器控制器提供RESTful API接口:

  • 播放进度管理: GET/POST/PUT/DELETE /player/progress
  • 播放列表获取: GET /player/audio/list 和 /player/audio/:id
  • 章节音频处理: 自动合并章节音频、权限控制
  • 最近播放记录: GET /player/recent

播放器服务 (Prisma ORM)

播放器服务层负责数据持久化和业务逻辑:

  • 播放记录存储: PlayRecord模型,支持upsert操作
  • 章节音频合并: 自动合并章节下的小节音频
  • 最近播放查询: 获取用户的最近播放记录
  • 权限验证: 确保用户只能访问自己的音频

章节来源

  • audio.ts:1-297
  • player.controller.ts:1-344
  • player.service.ts:1-280

架构概览

系统采用分层架构设计,确保关注点分离和代码可维护性:

graph TB
subgraph "表现层 (Presentation Layer)"
UI[播放器UI组件]
Mini[迷你播放器组件]
end
subgraph "应用层 (Application Layer)"
Controller[播放器控制器]
Service[播放器服务]
WebSocket[WebSocket服务]
end
subgraph "领域层 (Domain Layer)"
AudioMerger[音频合并器]
PlayRecord[播放记录模型]
BookChapter[章节模型]
end
subgraph "基础设施层 (Infrastructure Layer)"
Prisma[Prisma ORM]
MySQL[MySQL数据库]
FFmpeg[FFmpeg处理]
end
UI --> Controller
Mini --> Controller
Controller --> Service
Service --> AudioMerger
Service --> PlayRecord
Service --> BookChapter
Service --> Prisma
Prisma --> MySQL
AudioMerger --> FFmpeg
WebSocket --> UI

图表来源

  • index.vue:192-660
  • player.controller.ts:1-344
  • audio-merger.ts:1-86

详细组件分析

音频播放控制机制

音频播放控制机制通过Pinia状态管理实现,提供完整的播放生命周期管理:

sequenceDiagram
participant UI as 播放器UI
participant Store as 音频Store
participant AudioCtx as 音频上下文
participant API as 后端API
UI->>Store : play(audio)
Store->>Store : initAudioContext()
Store->>AudioCtx : 创建InnerAudioContext
Store->>AudioCtx : 设置事件监听器
Store->>AudioCtx : 设置音频源URL
Store->>AudioCtx : 开始播放
AudioCtx->>Store : onTimeUpdate
Store->>Store : 更新currentTime和duration
AudioCtx->>Store : onEnded
Store->>Store : 处理播放模式
Store->>API : 保存播放进度
UI->>Store : pause/resume
Store->>AudioCtx : 暂停/继续播放
UI->>Store : seek(time)
Store->>AudioCtx : 跳转到指定位置

图表来源

  • audio.ts:112-141
  • audio.ts:234-239

播放模式实现

系统支持四种播放模式,每种模式都有特定的行为逻辑:

播放模式 行为描述 实现要点
顺序播放 (sequence) 按列表顺序播放,播放完不自动下一首 基础播放模式,不自动切换
列表循环 (loop) 播放到最后一首后回到第一首 检查边界条件,循环播放
单曲循环 (single) 重复播放当前歌曲 重新播放当前音频
随机播放 (random) 随机选择下一首播放 生成随机索引,更新currentIndex

章节来源

  • audio.ts:183-212

播放列表管理

播放列表管理提供了灵活的音频组织和导航能力:

flowchart TD
Start([开始播放]) --> CheckPlaylist{检查播放列表}
CheckPlaylist --> |有播放列表| LoadPlaylist[加载播放列表]
CheckPlaylist --> |无播放列表| FetchGeneric[获取通用播放列表]
LoadPlaylist --> SetCurrent[设置当前音频]
FetchGeneric --> SetCurrent
SetCurrent --> InitAudio[初始化音频上下文]
InitAudio --> PlayAudio[开始播放]
PlayAudio --> NextCheck{检查下一首}
NextCheck --> |有下一首| PlayNext[播放下一首]
NextCheck --> |无下一首| ModeCheck{检查播放模式}
ModeCheck --> |单曲循环| SingleLoop[单曲循环]
ModeCheck --> |列表循环| LoopPlaylist[列表循环]
ModeCheck --> |随机播放| RandomPlay[随机播放]
ModeCheck --> |顺序播放| Stop[停止播放]
PlayNext --> PlayAudio
SingleLoop --> PlayAudio
LoopPlaylist --> PlayAudio
RandomPlay --> PlayAudio

图表来源

  • index.vue:386-437
  • audio.ts:249-256

播放列表数据结构

播放列表使用统一的AudioItem接口,支持多种音频来源:

interface AudioItem {
  id?: string | number;
  _id: string;
  audioId?: string;
  title: string;
  text: string;
  audioUrl: string;
  audioDuration: number;
  wordCount: number;
  isFavorite: boolean;
  albumId?: string;
  albumName?: string;
  lrcLyrics?: string;
}

章节来源

  • index.vue:415-424
  • index.ts:20-43

进度记录与恢复

进度记录系统提供了完整的播放进度跟踪和恢复功能:

sequenceDiagram
participant Audio as 音频播放器
participant Store as 状态管理
participant API as 后端API
participant DB as 数据库
Audio->>Store : onTimeUpdate
Store->>Store : 更新currentTime
loop 每30秒
Store->>API : POST /player/progress
API->>DB : 保存播放进度
DB-->>API : 确认保存
API-->>Store : 返回保存结果
end
Note over Audio,DB : 用户重新打开应用时
Audio->>API : GET /player/progress
API->>DB : 查询播放进度
DB-->>API : 返回进度记录
API-->>Audio : 返回最近播放进度
Audio->>Store : 恢复播放状态
Audio->>Audio : 从上次位置继续播放

图表来源

  • player.controller.ts:14-28
  • player.service.ts:39-81

播放记录数据模型

播放记录使用PlayRecord模型,支持用户级别的进度跟踪:

erDiagram
PLAYRECORD {
int id PK
int userId FK
int chapterId FK
float progress
float duration
datetime createdAt
datetime updatedAt
}
USER {
int id PK
string phone
string openid
string nickname
}
BOOKCHAPTER {
int id PK
int bookId FK
string title
string audioUrl
int audioDuration
}
USER ||--o{ PLAYRECORD : has
BOOKCHAPTER ||--o{ PLAYRECORD : has

图表来源

  • schema.prisma:63-77

章节来源

  • player.service.ts:10-34
  • schema.prisma:63-77

播放器状态管理

播放器状态管理采用响应式设计,确保UI与状态的实时同步:

stateDiagram-v2
[*] --> Idle : 初始化
Idle --> Loading : 开始加载音频
Loading --> Playing : 音频准备就绪
Loading --> Error : 加载失败
Playing --> Paused : 用户暂停
Paused --> Playing : 用户继续
Playing --> Ended : 播放完成
Ended --> Idle : 重置状态
Error --> Idle : 重试或取消
state Playing {
[*] --> Buffering : 缓冲中
Buffering --> Seeking : 用户跳转
Seeking --> Playing : 跳转完成
Playing --> Updating : 更新进度
Updating --> Playing : 进度更新完成
}

图表来源

  • audio.ts:34-74

状态同步机制

前端通过Pinia的响应式系统实现状态同步:

  • 计算属性: hasPlaylist、hasNext、hasPrev等基于当前状态动态计算
  • 事件监听: 音频上下文事件触发状态更新
  • 组件绑定: Vue模板直接绑定响应式状态,实现自动更新

章节来源

  • audio.ts:24-27
  • audio.ts:34-74

音频缓冲策略

系统实现了智能的音频缓冲策略,优化移动端播放体验:

flowchart TD
Start([开始播放]) --> CheckNetwork{检查网络状态}
CheckNetwork --> |WiFi| FullBuffer[完全缓冲]
CheckNetwork --> |移动网络| AdaptiveBuffer[自适应缓冲]
FullBuffer --> PlayDirect[直接播放]
AdaptiveBuffer --> CheckSize{检查音频大小}
CheckSize --> |小于10MB| Preload[预加载]
CheckSize --> |大于10MB| Stream[流式播放]
Preload --> PlayDirect
Stream --> PlayDirect
PlayDirect --> Monitor[监控播放状态]
Monitor --> BufferCheck{检查缓冲}
BufferCheck --> |不足| ExtendBuffer[延长缓冲]
BufferCheck --> |充足| Continue[继续播放]
ExtendBuffer --> Monitor
Continue --> End([播放完成])

图表来源

  • audio.ts:112-141

网络音频处理

网络音频处理支持多种音频格式和来源,包括OSS存储的音频文件:

sequenceDiagram
participant Client as 客户端
participant Controller as 播放器控制器
participant Service as 播放器服务
participant Merger as 音频合并器
participant Storage as 存储服务
Client->>Controller : 获取章节音频
Controller->>Service : 验证访问权限
Service->>Storage : 检查音频URL
Storage-->>Service : 返回音频信息
alt 章节音频
Service->>Merger : 检查是否需要合并
Merger->>Storage : 获取子章节音频
Storage-->>Merger : 返回子音频列表
Merger->>Merger : 合并音频文件
Merger-->>Service : 返回合并后的URL
end
Service-->>Controller : 返回音频详情
Controller-->>Client : 返回音频信息

图表来源

  • player.controller.ts:229-294
  • player.service.ts:147-242

章节来源

  • player.controller.ts:138-227
  • audio-merger.ts:11-36

播放器服务实现原理

播放器服务实现了完整的音频播放生命周期管理:

classDiagram
class PlayerController {
+getProgress(ctx) Promise
+saveProgress(ctx) Promise
+updateProgress(ctx) Promise
+deleteProgress(ctx) Promise
+getAudioList(ctx) Promise
+getAudioDetail(ctx) Promise
}
class PlayerService {
+getPlayProgress(userId, chapterId) Promise
+savePlayProgress(userId, chapterId, progress, duration) Promise
+updatePlayProgress(userId, chapterId, progress, duration) Promise
+deletePlayRecord(userId, chapterId) Promise
+mergeChapterAudios(chapterId) Promise
+getChapterAudioUrl(chapterId) Promise
+getRecentPlayRecords(userId, limit) Promise
}
class AudioMerger {
+merge(inputFiles, outputPath) Promise
+mergeLocalFiles(inputFiles, outputPath) Promise
+getDuration(filePath) Promise
}
class WebSocketService {
+addClient(clientId, ws) void
+removeClient(clientId) void
+sendToClient(clientId, event, data) boolean
+broadcast(event, data) void
+pushAudioGenerationComplete(bookId, chapterId, status) void
}
PlayerController --> PlayerService : 使用
PlayerService --> AudioMerger : 调用
PlayerService --> WebSocketService : 推送事件

图表来源

  • player.controller.ts:1-344
  • player.service.ts:1-280
  • audio-merger.ts:1-86
  • websocket.service.ts:1-136

章节来源

  • player.controller.ts:1-344
  • player.service.ts:1-280

前端状态同步机制

前端状态同步通过Vue的响应式系统和Pinia状态管理实现:

sequenceDiagram
participant Page as 播放器页面
participant Store as 音频Store
participant AudioCtx as 音频上下文
participant API as 后端API
Page->>Store : onShow生命周期
Store->>Page : 同步当前音频状态
Page->>AudioCtx : 获取实时播放状态
AudioCtx-->>Page : 返回播放进度
loop 每秒更新
AudioCtx->>Store : onTimeUpdate事件
Store->>Store : 更新currentTime
Store->>API : 异步保存播放进度
end
Page->>Store : 用户交互
Store->>AudioCtx : 执行播放控制命令
AudioCtx-->>Store : 返回执行结果
Store-->>Page : 更新UI状态

图表来源

  • index.vue:327-339
  • audio.ts:53-62

迷你播放器集成

迷你播放器提供后台播放支持,即使用户离开播放器页面也能控制播放:

flowchart LR
Page[播放器页面] --> |离开页面| Mini[迷你播放器]
Mini --> |点击| Player[播放器页面]
Mini --> |控制| Store[音频状态]
Store --> AudioCtx[音频上下文]
Store -.->|状态变化| Mini
AudioCtx -.->|播放状态| Store
Store -.->|播放状态| Page

图表来源

  • MiniPlayer.vue:33-46

章节来源

  • MiniPlayer.vue:1-166

播放历史记录管理

播放历史记录管理提供了用户播放历史的完整追踪:

erDiagram
USER {
int id PK
string phone
string nickname
}
PLAYRECORD {
int id PK
int userId FK
int chapterId FK
float progress
float duration
datetime updatedAt
}
BOOKCHAPTER {
int id PK
int bookId FK
string title
string audioUrl
int audioDuration
}
BOOK {
int id PK
int userId FK
string title
string coverUrl
}
USER ||--o{ PLAYRECORD : has
BOOKCHAPTER ||--o{ PLAYRECORD : has
BOOK ||--o{ BOOKCHAPTER : contains

图表来源

  • schema.prisma:63-77
  • schema.prisma:161-192
  • schema.prisma:130-159

章节来源

  • player.service.ts:250-279
  • schema.prisma:63-77

移动端播放适配

系统针对移动端进行了专门的适配优化:

响应式布局设计

播放器界面采用响应式设计,适配不同屏幕尺寸:

graph TB
subgraph "桌面端"
Desktop[宽屏布局<br/>完整播放列表<br/>歌词显示]
end
subgraph "移动端"
Mobile[紧凑布局<br/>滚动播放列表<br/>歌词自动滚动]
MiniPlayer[固定迷你播放器<br/>底部悬浮]
end
subgraph "平板端"
Tablet[中等布局<br/>侧边播放列表<br/>触摸优化]
end
Desktop --> Mobile
Desktop --> Tablet
Mobile --> MiniPlayer

触摸交互优化

  • 滑动控制: 进度条滑块支持精确控制
  • 手势支持: 支持滑动切换章节
  • 触摸反馈: 按钮点击提供视觉反馈
  • 安全区域: 考虑刘海屏和底部安全区域

章节来源

  • index.vue:662-800

后台播放支持

系统实现了完整的后台播放支持:

sequenceDiagram
participant User as 用户
participant App as 应用程序
participant System as 系统服务
participant Audio as 音频服务
User->>App : 关闭播放器页面
App->>System : 注册后台播放服务
System->>Audio : 启动音频播放
Audio-->>System : 播放状态更新
System-->>App : 后台播放通知
loop 播放过程中
Audio->>System : 进度更新
System->>App : 进度回调
App->>App : 更新状态管理
end
User->>App : 打开播放器页面
App->>System : 获取当前播放状态
System-->>App : 返回播放状态
App-->>User : 显示当前播放状态

图表来源

  • index.vue:342-348

睡眠定时功能

系统提供睡眠定时功能,支持定时停止播放:

flowchart TD
Start([开始播放]) --> SetTimer[设置睡眠定时]
SetTimer --> CountDown[倒计时进行中]
CountDown --> TimerTick{定时器滴答}
TimerTick --> |剩余时间>0| CountDown
TimerTick --> |剩余时间<=0| PauseAudio[暂停播放]
PauseAudio --> End([定时结束])

图表来源

  • index.vue:562-582

章节来源

  • index.vue:544-593

播放统计收集

系统实现了播放统计收集功能,为数据分析提供支持:

flowchart TD
PlayStart[开始播放] --> TrackStart[记录开始时间]
TrackStart --> ProgressUpdate[进度更新]
ProgressUpdate --> SaveProgress[保存进度]
SaveProgress --> CheckCompletion{检查播放完成}
CheckCompletion --> |未完成| ProgressUpdate
CheckCompletion --> |已完成| TrackEnd[记录结束时间]
TrackEnd --> CalculateDuration[计算播放时长]
CalculateDuration --> SaveStats[保存统计数据]
SaveStats --> SendAnalytics[发送分析数据]
SendAnalytics --> End([统计完成])

图表来源

  • player.service.ts:39-81

依赖关系分析

系统各组件之间的依赖关系清晰明确:

graph TB
subgraph "前端依赖"
Vue[Vue3框架]
Pinia[Pinia状态管理]
UniApp[UniApp跨平台框架]
Axios[HTTP客户端]
end
subgraph "后端依赖"
Koa[Koa框架]
Prisma[Prisma ORM]
WS[WebSocket]
FFmpeg[FFmpeg处理]
end
subgraph "数据库依赖"
MySQL[MySQL数据库]
Redis[Redis缓存]
end
Vue --> Pinia
Vue --> UniApp
Vue --> Axios
Koa --> Prisma
Koa --> WS
Prisma --> MySQL
Prisma --> Redis
FFmpeg --> Koa

图表来源

  • audio.ts:1-5
  • player.controller.ts:1-6

章节来源

  • audio.ts:1-5
  • player.controller.ts:1-6

性能考虑

内存管理

系统采用智能的内存管理策略:

  • 音频上下文复用: 避免频繁创建和销毁音频实例
  • 状态清理: 页面卸载时清理定时器和事件监听器
  • 资源释放: 音频播放结束后及时释放相关资源

网络优化

  • CDN加速: 音频文件通过CDN分发,提升加载速度
  • 断点续传: 支持音频文件的断点续传
  • 缓存策略: 实现多层次的缓存机制

数据库优化

  • 索引优化: 为常用查询字段建立索引
  • 连接池: 使用连接池管理数据库连接
  • 查询优化: 优化复杂查询语句

故障排除指南

常见问题及解决方案

音频无法播放

可能原因:

  • 音频URL无效或过期
  • 网络连接问题
  • 浏览器兼容性问题

解决步骤:

  1. 检查音频URL的有效性
  2. 确认网络连接正常
  3. 尝试刷新页面或重启应用
  4. 检查浏览器控制台错误信息

播放进度不同步

可能原因:

  • 网络延迟导致的进度不同步
  • 设备时钟不同步
  • 缓冲区问题

解决步骤:

  1. 检查网络连接稳定性
  2. 重新加载音频文件
  3. 清除浏览器缓存
  4. 检查设备时间设置

播放列表不显示

可能原因:

  • 权限不足
  • 网络请求失败
  • 数据格式错误

解决步骤:

  1. 确认用户登录状态
  2. 检查API响应状态
  3. 查看控制台错误日志
  4. 重新获取播放列表

章节来源

  • audio.ts:64-74
  • player.controller.ts:253-259

结论

音频播放系统是一个功能完整、架构清晰的移动端音频播放解决方案。系统采用现代化的技术栈,提供了优秀的用户体验和良好的扩展性。

主要优势

  1. 完整的播放功能: 支持多种播放模式、进度控制、歌词同步
  2. 智能状态管理: 基于Pinia的状态管理,确保UI与状态的实时同步
  3. 完善的进度跟踪: 提供播放进度记录和恢复功能
  4. 移动端优化: 针对移动端进行了专门的适配和优化
  5. 后台播放支持: 支持应用切换到后台时继续播放

技术亮点

  • 响应式设计: 适配多种设备和屏幕尺寸
  • WebSocket集成: 实现实时状态同步和事件推送
  • 音频合并功能: 自动合并章节音频,提升用户体验
  • 权限控制: 完善的访问权限验证机制

未来改进方向

  1. 离线播放: 支持音频文件的离线缓存和播放
  2. 多设备同步: 实现多设备间的播放状态同步
  3. 个性化推荐: 基于播放历史的音频推荐算法
  4. 无障碍支持: 增强对残障用户的无障碍访问支持

附录

API接口规范

系统提供以下核心API接口:

接口 方法 描述 参数
/player/progress GET 获取播放进度列表 audioId(可选)
/player/progress POST 保存播放进度 audioId, progress, duration
/player/progress/:audioId PUT 更新播放进度 progress, duration(可选)
/player/progress/:audioId DELETE 删除播放记录
/player/audio/list GET 获取播放列表 page, pageSize
/player/audio/:id GET 获取音频详情

数据模型说明

系统使用以下核心数据模型:

  • PlayRecord: 播放记录模型,记录用户的播放进度
  • BookChapter: 章节模型,包含音频文件信息
  • User: 用户模型,管理用户基本信息
  • Playlist: 播放列表模型,支持用户自定义播放列表

配置选项

系统支持以下配置选项:

  • 播放速度: 支持0.5x到2x的播放速度调节
  • 播放模式: 顺序播放、列表循环、单曲循环、随机播放
  • 音质设置: 支持不同的音频质量选择
  • 缓存策略: 可配置的音频缓存和清理策略