v0.2.0-rc4
Pre-release
Pre-release
Added
- 管理后台配置迁移页:新增
src/admin/views/ConfigTransferView.vue,并在AdminSidebar中加入「迁移」导航项(FileJson图标);页面提供「导出未加密配置」和「导入未加密配置」两块操作面板,导出前调用validateMusicConfig(props.config)对当前表单配置做完整 schema 校验,通过后生成meliora-config-plain-<ISO 时间>.json;导出确认弹窗明确提示明文 JSON 会包含站点配置、音乐 API Token、GitHub 代理、预发布开关等敏感字段,同时说明管理员登录密码不会被反向导出;导入流程通过隐藏<input type="file" accept="application/json,.json">读取 JSON,解析失败提示「JSON 格式无效」,schema 校验失败时拼接具体错误,校验通过后先进入「覆盖当前配置?」确认弹窗,确认后仅更新后台表单并提示「确认无误后请保存」,不会直接提交到仓库 - 桌面侧边栏折叠模式:
AdminSidebar新增desktopCollapsed状态与meliora:admin-sidebar-collapsedlocalStorage 持久化键,桌面端可通过PanelLeftClose/PanelLeftOpen图标按钮在 200px 完整导航和 72px 图标导航间切换;折叠后隐藏品牌文案和 tab 文案,保留图标、tooltip、保存/退出/返回按钮的可访问标题;移动端强制恢复完整 sticky 顶栏布局,避免折叠状态污染手机导航 - 音乐来源适配器层:新增
src/services/music-adapters/types.ts定义MusicProviderAdapter<TSource>与MusicProviderContext;新增local.ts/meting.ts两个 adapter,分别处理本地曲目映射和 Meting 歌单拉取;loadConfiguredTracks支持通过LoadConfiguredTracksOptions.adapters注入测试/自定义 adapter,并把每个来源统一封装为ConfiguredSource.load(config)任务,后续新增其他音乐来源时不再需要把逻辑塞进src/services/music.ts - 曲目级歌词 Provider 注册表:
src/services/lyrics.ts新增TrackLyricsProvider、registerTrackLyrics、loadTrackLyrics、hasTrackLyricsSource、hasCachedTrackLyrics、transferTrackLyricsProvider、mergeTrackLyricsProvider;歌词来源从Track.lyricsUrl字段迁移到 WeakMap 注册表,本地曲目 provider 优先级为 20,Meting provider 优先级为 10,去重时高优先级 provider 会保留到最终曲目;loadLrcLyrics统一负责loadLyricsText+parseLyrics+ 空歌词过滤 - 分享别名与标题版本兼容:
Track类型新增titleVersions?和shareAliases?,并将kind扩展为'meting' | 'remote' | 'local';mapMetingTrack/mapLocalTrack通过splitDisplayTitle把标题中的版本信息拆入titleVersions,formatTrackDisplayTitle用于显示和分享文案;trackIdentity将主标题和所有版本标题一起参与归一化,trackMatchesShareId同时匹配当前哈希和历史shareAliases,保证旧版基于原始标题生成的?share=链接在标题拆分后仍能定位歌曲 - 更新 workflow 防护测试:新增
server/tests/update-workflow.test.ts,静态校验.github/workflows/update-from-upstream.yml必须包含 GitHub proxy 输入校验、curl --max-redirs 0、以及合并目标分支后重新执行pnpm install --frozen-lockfile、pnpm test、pnpm type-check、pnpm lint、pnpm format:check、pnpm build、pnpm test:bundle - 音乐 API 重定向测试:新增
server/tests/music-api-tester.test.ts,覆盖后台音乐 API 测试遇到 302 重定向时不会跟随到内网/元数据地址,并返回error: '重定向已拒绝' - 配置迁移、管理导航和播放器测试补充:新增
src/tests/admin-config-transfer.test.ts覆盖迁移入口、桌面侧边栏折叠恢复、明文配置导出风险确认、合法 JSON 导入覆盖确认、非法 JSON 校验失败;新增/扩展about-update-status、admin-about-navigation、dom-utils、settings-controls-a11y、track-lyrics-service、use-drawer-sheet、use-keyboard-shortcuts等测试,覆盖本次 UI 与数据流改动
Changed
- 管理后台页面壳层重组:
DashboardView.vue新增.dashboard-shell包裹AdminSidebar和dashboard-content,桌面端保持横向布局,760px 以下改为纵向布局;tab 切换新增transfer分支,将ConfigTransferView接入主后台并复用现有showMessage通知;确认弹窗和 Toast 移入 shell 内部,确保迁移页、关于页和设置页的遮罩层级一致 - 关于页更新轮询更精细:
AboutView.vue调用checkUpdate时传入{ markUnauthenticated: openModalOnUpdate },避免静默轮询状态时误把认证弹窗打断到前台;refreshUpdateStatus开始读取服务端返回的retryAfterSeconds,当 workflow 状态为locating/queued/running时优先使用服务端建议的轮询间隔,否则回退到 3s/5s;模板外层增加.about-root,让关于页在后台 shell 中布局更稳定 - 管理后台表单可访问性补强:
AdvancedSettingsEditor的预发布开关、AnalyticsSettingsEditor的 Umami/Google Analytics 开关、PlaylistEditor的歌单启用开关均补充明确aria-label,让图标/开关控件在读屏器中不再只有泛化状态 - 播放器歌词加载链路改为按曲目加载:
LyricsPanel.vue不再监听currentTrack.lyricsUrl,改为监听[currentTrack.id, currentTrackVersion]并调用loadTrackLyrics(currentTrack);歌词缓存判断改为hasCachedTrackLyrics(track),空歌词/无 provider 统一进入empty;usePreloadPool和useAudioPlayer的歌词预加载也改为传入完整Track,使本地曲目、Meting 曲目和未来 adapter 曲目的歌词来源都走同一套 provider 机制 - 当前曲目对象保活更新:
src/stores/player.ts新增currentTrackVersion;setTracks在刷新曲库时若当前歌曲仍存在,保留原activeTrack引用并把新字段Object.assign回去,同时调用transferTrackLyricsProvider(track, activeTrack)迁移歌词 provider 并递增currentTrackVersion;这样可以避免音频播放中的当前曲目对象被替换导致播放状态、歌词和预加载出现短暂脱节 - 曲目去重保留元数据:
deduplicateTracks从Set改为Map<string, Track>,新增mergeDuplicate回调;loadConfiguredTracks在远程/本地来源去重时会合并歌词 provider 和分享别名,避免同一首歌在多个来源出现时丢失歌词来源或旧分享链接兼容信息 - 搜索和展示支持标题版本:
filterTracks会同时匹配track.titleVersions;TrackList不再对track.title现场调用splitDisplayTitle,而是直接消费track.title与track.titleVersions,避免列表渲染时重复拆分标题;播放器顶部标题同样改为从currentTrack.titleVersions读取版本信息 - 分享文案统一使用展示标题:
useTrackShare从splitDisplayTitle(track.title)改为formatTrackDisplayTitle(track),分享标题包含主标题和版本信息,分享 URL 继续使用?share=<createTrackShareId(track)>,但打开链接时PlayerView通过trackMatchesShareId匹配当前哈希或历史别名 - 设置面板空曲库禁用:
PlayerView.vue新增settingsAvailable = tracks.length > 0;没有歌曲时设置按钮禁用,aria-label/title显示「暂无歌曲,设置不可用」,如果曲库变空时设置面板已打开会自动关闭,避免空曲库下进入无意义设置抽屉 - 顶栏点击关闭面板更稳:
PlayerView.onTopbarClick改用isInteractiveElement(e.target)判断交互元素,替代手写closest('button, a, [role="button"], input, label'),让 label、输入框、按钮及后续扩展的交互控件不误触发关闭面板 - 开发生成流程统一:
package.json新增generate:dev,将predev、pretype-check、pretest、pretest:watch、prelint统一改为pnpm generate:dev;test:bundle显式加入脚本,本地与 CI 都能用同一命令检查构建产物泄漏 - CI 质量门禁加严:
main-validation.yml与pr-validation.yml在pnpm build后新增pnpm test:bundle;update-from-upstream.yml在验证阶段和合并目标分支后都加入 bundle 泄漏检查,并在最终合并前补跑pnpm install --frozen-lockfile、lint、format:check,减少临时分支落后目标分支时绕过质量门禁的可能 - GitHub proxy 输入和请求策略收紧:
update-from-upstream.yml新增Validate GitHub proxy input步骤,拒绝非 http(s)、无 hostname、localhost、.localhost、.local、私有/回环/link-local/组播/reserved/unspecified IP;workflow 中经代理访问 GitHub API 的curl改为--max-redirs 0,避免代理或恶意响应把请求重定向到非预期地址 - bundle 泄漏测试显式执行时必须有 dist:
server/tests/bundle-leakage.test.ts增加explicitBundleCheck,只有普通全量测试且dist不存在时才 skip;当用户显式运行pnpm test:bundle时如果dist缺失会直接失败,避免误以为检查已执行
Fixed
- 歌词请求取消误伤共享缓存:
loadLyricsText调整 Abort 语义:缓存层内部AbortController只负责 8 秒超时,调用方传入的signal只取消当前等待(withAbortSignal),不再把外部 abort 转发到共享 fetch;修复快速切歌、预加载和面板重载同时请求同一个歌词 URL 时,一个消费者取消会把其他消费者一起取消的问题 - 更新检查拒绝 GitHub 重定向:
server/core/update-handler.ts对 latest release、tags、workflow runs、workflow dispatch 请求均设置redirect: 'manual';当 GitHub 或代理返回 3xx 时,检查更新映射为400并提示「GitHub 代理重定向已拒绝,请使用直连的公网代理地址」,防止代理配置把服务端请求带到非预期目标 - 音乐 API 测试拒绝重定向:
server/core/music-api-tester.ts的fetchWithTimeout增加redirect: 'manual',歌单测试遇到 3xx 时返回失败结果、保留状态码并显示「重定向已拒绝」,避免后台测试接口跟随重定向访问内网地址 - 当前曲目刷新后歌词不更新:曲库重新加载时如果当前曲目 ID 不变但歌词 provider、标题版本或分享别名发生变化,现在通过
currentTrackVersion触发LyricsPanel重新加载歌词,同时保留当前播放对象引用,修复配置更新/远程歌单刷新后歌词仍使用旧来源的问题 - 旧分享链接在标题拆分后失效:
mapMetingTrack/mapLocalTrack会根据拆分前原始标题生成 legacy share ID,当它与新 share ID 不一致时写入shareAliases;PlayerView打开?share=时同时匹配别名,修复标题从「歌名 (版本)」拆为title + titleVersions后旧链接找不到歌曲的问题 - 空曲库仍可打开设置:设置按钮在
tracks.length === 0时禁用并自动关闭已打开的设置面板,避免没有任何歌曲时打开设置抽屉造成误导 - 明文配置导入/导出类型构建问题:
admin-config-transfer.test.ts改为显式保存导出的 Blob holder,并使用find(...).exists()做 Vue Test Utils 断言,修复vue-tsc -b在项目引用构建下对可选链和get().exists()的类型误判 - 音乐 adapter 联合泛型构建问题:
src/services/music.ts将ConfiguredMusicSource<T>联合改为闭包式ConfiguredSource.load(config),消除ConfiguredMusicSource<LocalTrackConfig>被错误传给ConfiguredMusicSource<MetingPlaylistConfig>的 TypeScript 构建错误