当前项目里的统一播放控制入口是 MusicPlaybackController,但它本质上仍然只是一个动作分发器或事件桥接层:
playOrPause、playNext、playPrevious、seekTo 等动作依赖 LocalMusic 在页面生命周期里注册 actions。EVENT_PLAYLIST_PLAY 发送请求,再由 LocalMusic 监听并执行真实播放。LocalMusic 内部。这导致一个结构性问题:任何页面想直接调用 controller 播放,实际上仍然依赖 LocalMusic 充当“播放器宿主页面”。
第一阶段只解决“播放核心依赖 LocalMusic 宿主”的问题,不做整站 UI 重构。
完成标准:
MusicPlaybackController 起播单曲或队列。MusicPlaybackController 调用 play/pause/next/previous/seek。LocalMusic 不再负责接收 EVENT_PLAYLIST_PLAY 作为主播放入口。NewIndex/LocalMusic 层。LocalMusic 继续常驻隐藏挂载,但它不再是播放指令的宿主。非目标:
LocalMusic 的所有播放相关字段。采用“抽离 PlaybackCoordinator,保留现有 UI 宿主”的方案。
核心思想:
MusicPlaybackController 升级成真正的统一控制入口。PlaybackCoordinator 承接真实播放核心逻辑。LocalMusic 从“播放逻辑宿主”降级为“播放 UI 容器”。EVENT_PLAYLIST_PLAY -> LocalMusic 这条链路起播。不采用以下方案:
职责:
PlaybackCoordinator。第一阶段期望接口:
playSong(song: VideoItem, options?)playQueue(songs: VideoItem[], startIndex: number, options?)playOrPause()playNext()playPrevious()seekTo(value: string, source?: string)兼容策略:
setActions/clearActions 暂时保留,但标记为过渡能力。PlaybackCoordinator;仅对尚未迁出的能力允许短期回退。新增协调层,负责播放核心链路。
职责:
第一阶段必须迁出的逻辑:
doPlay 主链路及其前后置准备逻辑。play/pause/next/previous/seek 的控制主路径。第一阶段允许仍留在 UI 层的逻辑:
迁移后职责:
PlaybackCoordinator 暴露的播放状态并渲染 UI。迁移后不再承担的职责:
EVENT_PLAYLIST_PLAY 作为主播放入口。保持当前职责:
迁移前:
LocalMusic 监听事件。LocalMusic 执行 doPlay 与后续播放流程。迁移后:
MusicPlaybackController.playSong(...) 或 playQueue(...)。PlaybackCoordinator。PlaybackCoordinator 更新播放队列、当前歌曲、底层播放器与共享播放状态。LocalMusic/NewIndex 作为 UI 消费者响应状态变化。关键变化:
LocalMusic 从命令执行者变成状态消费者。为降低风险,第一阶段只抽“核心播放状态”,不抽“全部 UI 状态”。
核心播放状态建议包含:
currentSongsongListcurIndexisPlaying继续留在 UI 层的状态:
判断标准:
目标:
LocalMusic 中抽离 doPlay 主链路到 PlaybackCoordinator。playSong(song) 和 playQueue(songs, startIndex)。ChartsCount、歌单页、发现页等最直接的列表页。验收:
EVENT_PLAYLIST_PLAY 也能直接起播。目标:
playOrPause、playNext、playPrevious、seekTo 从 LocalMusic 挪到 PlaybackCoordinator。MusicPlaybackController.setActions/clearActions 进入兼容态,不再作为主路径。验收:
LocalMusic 注册 actions。目标:
LocalMusic 改为从共享状态读取当前歌曲、索引、播放状态、进度等。验收:
LocalMusic 只做 UI 响应,不再承担核心状态源头角色。目标:
EVENT_PLAYLIST_PLAY 作为本地主播放主链的职责。验收:
为了避免一次改坏所有场景,第一阶段允许短期双轨:
controller -> coordinator。但有两条硬约束:
EVENT_PLAYLIST_PLAY -> LocalMusic 依赖。LocalMusic 当前很可能把播放器状态、UI 状态、动画状态、投播状态混在同一个大组件中。
应对:
如果 AVSession 与投播流程深度耦合 LocalMusic 内部字段,直接搬迁会有回归风险。
应对:
若 coordinator 成为状态源,但 LocalMusic 仍在局部直接写旧字段,容易出现 UI 与实际播放状态分裂。
应对:
第一阶段验证重点不是全量 UI,而是播放主链切换是否成功。
必须验证:
play/pause/next/previous/seek 不依赖 LocalMusic actions。LocalMusic 不再作为 EVENT_PLAYLIST_PLAY 的主执行入口。建议测试层次:
本阶段采用以下明确决策:
LocalMusic 继续常驻隐藏挂载。LocalMusic 作为播放指令宿主。NewIndex/LocalMusic 的 UI 显示状态。这保证第一阶段的目标聚焦为:让 controller 真正变成任何页面都能直接调用的播放入口,同时把风险控制在可验证范围内。