Commit Graph

624 Commits

Author SHA1 Message Date
alger edc7e70158 fix(player): 私人 FM 模式下点击上一曲不再重放当前歌曲
_prevPlay 补充 FM 分支:FM 不支持回到上一首,与下一曲行为一致直接拉取新的 FM 歌曲。
此前 FM 列表只有一首歌,上一曲计算出的索引仍是当前曲目导致原地重放。

Closes #682
2026-07-05 14:09:51 +08:00
alger da80dbc761 fix(main): 修复 config.json 并发读写导致的 EBUSY 主进程崩溃弹窗
- 主进程 6 个独立 Store 实例合并为共享单例(window-size/lyric/server/deviceInfo/window 复用 config.ts 实例)
- initializeConfig 幂等化,修复 set-store-value IPC 监听重复注册导致的双倍写入
- 窗口/歌词窗 move、resize 保存增加 500ms 防抖,拖动时不再每秒几十次写盘,close 时冲刷落盘
- store 读写 IPC handler 与窗口状态读写加 try-catch,撞锁丢弃单次写入而非崩溃
- 全局 uncaughtException 兜底:文件锁类错误(EBUSY/EPERM 等)仅记日志,其余异常保留报错弹窗

Closes #714
2026-07-05 14:06:01 +08:00
alger 837f0268d5 fix(player): 合并外部贡献后的集成修复
- PlayBar 下载按钮补 isElectron 守卫,Web 端不再显示无效按钮(#708 评审意见)
- preloadNextSongs 恢复 800ms 短去抖,避免快速切歌时音源请求风暴(#712 评审意见)
- 清理合并后未使用的导入
2026-07-05 14:00:07 +08:00
alger 8ea13e04f0 Merge pull request #712 from daili115/trae/agent-m1V9o5
feat: 优化音乐播放性能,实现异步加载和即时预加载
2026-07-05 13:55:34 +08:00
alger 91a3259e27 Merge pull request #708 from jiang-LJ/main
feat(player): 在播放栏添加下载当前歌曲按钮
2026-07-05 13:55:34 +08:00
alger ecb85f0146 Merge pull request #676 from chengww5217/refactor/persisted-storage
refactor(persist): localStorage 配额防护与历史持久化重写
2026-07-05 13:55:20 +08:00
alger 6bea735aef Merge pull request #675 from chengww5217/fix/local-music-playback
fix(local-music): 修复本地音乐播放失败、进度异常与切歌失效
2026-07-05 13:55:20 +08:00
daili115 c5035b0583 feat: 修复软件切歌慢问题
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
2026-06-25 07:41:15 +00:00
jiang-LJ 7792eeac2e feat(player): 在播放栏添加下载当前歌曲按钮 2026-06-17 21:57:01 +08:00
chengww 938c497ca3 chore: 修复 main 上的存量 lint 错误
pr-check.yml 只在 PR 触发,main push 不跑 lint,导致存量错误一直未被发现,
PR #675 / #676 都被 Code Quality job 拦下。

- 9 处 prettier/prettier 格式错误:StickyTabPage.vue / RadioCard.vue /
  list/index.vue / mv/index.vue / podcast/index.vue 的属性换行与 import
  排序,全部 eslint --fix 自动修复
- 1 处 no-undef ScrollToOptions(StickyTabPage.vue:79):在 eslint.config.mjs
  的 .ts 与 .vue 配置块同时补 ScrollToOptions 到 globals,与既有的
  ScrollBehavior 同级;TypeScript DOM lib 类型不在 globals.browser 里,
  需手动声明
2026-05-17 23:54:28 +08:00
chengww 405b144e66 refactor(playHistory): v1 旧 localStorage key 清理与调用方对齐
老用户升级后清掉 v1 时代独立 key 释放配额,调用方切到 store API。

- player.ts initializePlayState 调用从 migrateFromLocalStorage 切到
  cleanupLegacyPlayHistoryStorage:不再做数据迁移(历史是低关键性
  衍生数据,老用户重新听几次即可),仅清掉旧 musicHistory/podcastHistory/
  playlistHistory/albumHistory/podcastRadioHistory/playHistory-migrated/
  playMode 这 7 个旧 key,避免和新 play-history-store key 双倍占用
- SystemTab.vue 清缓存的 case 'history' 改走 usePlayHistoryStore().
  clearMusicHistory(),与 v1 时代行为对齐(只清音乐历史,不动 podcast/
  playlist/album/podcastRadio);移除 case 'settings' 里的
  removeItem('playMode')——已并入 player-core-store
2026-05-17 23:08:52 +08:00
chengww 761884f23a refactor(playHistory): 持久化重写,统一防抖落盘与序列化兜底
把 playHistory 接入 utils/debouncedStorage 与 utils/persistedSong,
配合 add* 方法重构与 clearAll 同步落盘,闭合 localStorage 配额防护。

- musicHistory 类型从 SongResult 收敛到 MusicHistoryItem(精简子集),
  导出 MinifiedDjProgram、stripBase64Covers,给 podcast/playlist/album/
  podcastRadio 历史也做顶层 picUrl/coverImgUrl/coverUrl 的 base64 兜底
- serializePlayHistoryState 提取为模块级函数,给 persistedstate.serializer
  与 clearAll 同步落盘共用,避免格式漂移;isPodcast/program 字段必须
  保留——playbackController.playTrack 用 isPodcast 决定写哪条历史
- 5 个 add* 全部重写成单步 ref 重赋值,避免 splice/pop/unshift 多次
  触发 watch 与持久化;命中已有条目时累加 count + 刷新 lastPlayTime,
  picUrl/al 用新数据覆盖(封面可能换了短引用)
- clearAll 增加 flushDebouncedStorage + 同步 setItem 空状态,防止
  kill -9 落在 2s 防抖窗口里导致旧历史残留
- heatmap/index.vue 类型切到 MusicHistoryItem,移除 music.artists
  兜底(minifySong 已合并 ar/artists,只剩 ar)
2026-05-17 23:08:22 +08:00
chengww 537e280fdd refactor(persist): 抽公共防抖 storage 与 minifySong 工具,playlist/playerCore 接入
把 9caaec3 在 playlist 内联的 minifySong + debouncedLocalStorage 抽到
utils 共用,playerCore 同步接入,为后续推广到 playHistory 做准备。

- 新增 utils/debouncedStorage.ts:高频状态变更(volume 拖动、isPlay
  切换)2s 防抖落盘,beforeunload 钩子兜底刷盘;多 store 共用同一
  pendingWrites map,flush 时一次写完所有 pending key
- 新增 utils/persistedSong.ts:minifySong 剥离 base64 picUrl、仅保留
  local:// 永不过期的 playMusicUrl;ar.length 守卫避免空数组吞掉
  s.artists 兜底;al.id 守卫避免序列化出无 id 空壳
- playlist.ts 删除内联实现改用 import,行为不变
- playerCore.ts 切到防抖落盘并 minify playMusic;id 守卫避免空
  playMusic 走 minify 后失去 Object.keys().length===0 判空能力,
  下次启动会误恢复一首无 id 的空歌

Trade-off:极端非正常退出(kill -9 / 断电)下最近 2s 的 volume /
isPlay / playMusic 变更会丢——这些状态丢一次无大碍,可接受
2026-05-17 23:07:58 +08:00
chengww 15258f28fd fix(local-music): 封面落盘 + URL 编码统一,修复持久化配额与编码边界
- 新增 src/shared/localUrl.ts 共用 local:// 编码:按路径段 encodeURIComponent,
  避免整体编码把 / 转成 %2F 引发 Chromium 解析边界差异,同时正确处理
  空格/中文/# 等特殊字符(封面落到含空格目录时 Image loader 会加载失败)
- 封面从内嵌 base64 Data URL 改为 userData/AudioCovers/<sha256>.<ext> 落盘,
  MAX_COVER_BYTES 1MB→8MB;老条目(无 coverPath 字段)扫描时一次性自愈
- playlist minify 剥离 base64 picUrl 并仅持久化 local:// 永不过期的 playMusicUrl,
  防止单张 base64 封面撑爆 localStorage 5MB 配额导致整个 playList 写入失败;
  localStorage 写入加 try/catch 兜底,避免配额超限时直接抛异常
2026-05-17 23:07:17 +08:00
chengww fa818a020f fix(player): 双击歌曲时同步设置播放列表上下文
BaseSongItem 双击和右键菜单"播放"只调了 setPlay,未 emit 'play',
导致父组件 setPlayList 不会执行,上下一首切换失效。抽出统一入口
先 emit 再触发播放,与点击播放按钮行为对齐。
2026-05-17 23:07:01 +08:00
chengww c15a10c098 fix(local-music): 主进程实现 Range/206 让本地音乐 seek/快进恢复
切到 protocol.handle 后,audio 元素发的 Range 请求会带 HTTP 头到达
主进程;net.fetch(file://) 既不识别 Range 也不带 Accept-Ranges,导致
浏览器认为 local:// 不支持随机访问,seek 时只能从头加载,表现为
快进失效、退出后恢复播放从 0 开始。

新增 buildLocalFileResponse:
- 无 Range:200 + Accept-Ranges: bytes,让 audio 知道支持随机访问
- 有 Range:206 + Content-Range,用 fs.createReadStream 切片返回
- 非法 Range:416 + Content-Range: bytes */total

顺手把 fs.existsSync 换成 stat,一次拿文件大小且过滤掉目录。
2026-05-17 23:07:01 +08:00
chengww 0c0189bcef fix(local-music): 注册 local 协议为特权 scheme 修复本地音乐 CORS 报错
- main/index.ts 在 app.whenReady 之前调用 registerSchemesAsPrivileged,
  把 local 标记为 standard/secure/supportFetchAPI/stream/corsEnabled/bypassCSP,
  让 http 页面(dev server / 生产)可跨协议加载本地音频
- fileManager.ts 用 Electron 25 起推荐的 protocol.handle 替代已弃用的
  registerFileProtocol,内部转发到 file:// 自动支持 Range / 流式播放
- 修复 macOS/Linux 上去掉前导斜杠后丢失绝对路径前缀的问题
2026-05-17 23:07:01 +08:00
alger ee98eb0266 fix(player): 私人 FM 模式下点击下一首按钮可正常切歌
FM 播放列表只保留 1 首,原 _nextPlay 走到"顺序模式 + 最后一首"
分支只弹"列表已播完"提示,仅 audioService end 事件中拉取下一首
FM 的逻辑生效,导致用户手动点击下一首无效(issue #666)。

抽出 _nextFmPlay,_nextPlay 入口检测 isFmPlaying 直接路由到 FM
分支;MusicHook end 事件去掉重复的 FM 处理,统一走 nextPlayOnEnd。
2026-05-10 13:00:55 +08:00
alger 5ba9e6591a refactor(lyric): 抽取全屏背景/文字颜色逻辑为 useLyricBackground composable
MusicFull.vue 与 MusicFullMobile.vue 各自持有的 setTextColors /
currentBackground / animationFrame / isDark 合并到共享 composable,
消除两份几乎一致的包装逻辑。Mobile 的 --bg-color 差异通过 writeBgColor
option 注入,行为等价。
2026-05-10 12:34:53 +08:00
Alger 7e95ab69be Merge pull request #654 from chengww5217/fix/unmute-restore-volume
fix(player): 静音保留原音量,解除后可恢复
2026-05-10 12:23:34 +08:00
chengww 2b1024ca24 fix(player): 静音保留原音量,解除后可恢复
- playerCore 新增持久化 isMuted 状态及 setMuted/toggleMute,静音时音频输出置 0 但 volume 保持不变
- 音量 > 0 时自动解除静音
- useVolumeControl 移除原 0↔30 切换;滑块/百分比展示真实音量,图标反映静音态
- 三个播放栏的音量滑块在静音时 disabled;PlayBar 百分比文字同步置灰(仅文字颜色)
2026-04-26 21:47:11 +08:00
chengww a62f525840 fix(download): 下载设置抽屉打开时路径显示为空
get-store-value 主进程用 ipcMain.on 同步返回,renderer 却用 invoke 读取会直接
reject,导致 initDownloadSettings 中断、已保存的下载路径等配置回读失败。改为
sendSync,与项目其它调用点保持一致。

contentLength 类型错误非本次提交引入,因无法通过 precommit 检查,故一并修复。
2026-04-26 21:05:59 +08:00
alger 97220761cf fix(lyric): 重启后桌面歌词显示无歌词
playerCore.playMusic 整体替换 (id 不变) 时, lyric watcher 的响应式追踪不可靠
点击播放走 playTrack 流程也未必能可靠触发解析

- 提取 ensureLyricsLoaded 到 module 级别, 直接读 playMusic.value 解析
- openLyric 入口主动调用 (重启首次打开桌面歌词场景)
- onLyricWindowReady 兜底 (窗口异步就绪时 lyric 字段可能刚到位)
- audioService.on('play') 兜底 (重启后首次点击播放场景)
- 在线歌曲 lyric 字段缺失时, 主动调 getMusicLrc API 兜底
2026-04-19 16:15:09 +08:00
alger 7282e876f4 fix(lyric-window): 锁定状态启动时同步穿透并禁用 resize
- 重启应用恢复锁定时, 主进程主动 setIgnoreMouseEvents(true), 修复鼠标无法穿透
- 锁定时 setResizable(false), 隐藏窗口边缘 resize 光标
- 由 set-lyric-lock-state IPC 统一驱动, 后续 polling 按位置精细纠正
2026-04-19 15:44:28 +08:00
alger 6b22713854 perf(lyric-window): 仅在锁定+可见时启用鼠标位置轮询
- 新增 set-lyric-lock-state IPC, 渲染端 watch isLock 同步到主进程
- 主进程通过 isLyricLocked + isLyricWindowVisible 控制轮询启停
- 监听窗口 show/hide/minimize/restore, 隐藏时停止 50ms 轮询
- 解锁状态下 DOM mouseenter/mouseleave 已足够, 无需轮询兜底
2026-04-19 15:31:23 +08:00
kimjiwon e066efb373 add main-process cursor presence sync for locked lyric window 2026-04-12 13:29:02 +08:00
alger 030a1f1c85 fix(mpris): 修复 MPRIS 模块多项安全和性能问题
- 将 fix-sandbox.js 从 postinstall 移除,避免 npm install 时执行 sudo
- 修复 play/pause/stop 事件语义错误,不再全部映射到 togglePlay
- 缓存平台信息避免 sendSync 阻塞渲染进程
- 修复 cleanupAppShortcuts 中缺少 MPRIS 监听器清理导致的事件泄漏
- destroyMpris 中添加 IPC 监听器清理
- 清理冗余调试日志,安全加载 dbus-native 模块
- 添加 mpris-service 类型声明解决跨平台类型检查问题
2026-04-11 22:37:26 +08:00
alger 33fc4f768c 1. 实现linux下的mpris和gnome状态栏歌词功能 2026-04-11 15:45:14 +08:00
alger 8e3e4e610c fix(pwa): 修复 manifest.json 未被引用导致浏览器无法识别 PWA (#640)
在 index.html 中添加 manifest 引用,并补全 PWA 必需字段
2026-04-10 23:27:19 +08:00
alger 03b52cd6e2 fix(audio): 移除不必要的麦克风权限请求 (#639)
枚举音频输出设备时不再调用 getUserMedia,避免安全软件误报
2026-04-10 23:27:12 +08:00
4everWZ 8726af556a perf: 长列表渐进式渲染优化与播放栏遮挡修复 (#589)
- 新增 useProgressiveRender composable,提取手工虚拟化逻辑(renderLimit + placeholderHeight)
- FavoritePage/DownloadPage 使用 composable 实现渐进式渲染,避免大量 DOM 一次性渲染
- MusicListPage 初始加载扩大至 200 首,工具栏按钮添加 n-tooltip,新增回到顶部按钮
- 播放栏动态底部间距替代 PlayBottom 组件,修复播放时列表底部被遮挡
- 下载页无下载任务时自动切换到已下载 tab
- i18n: 添加 scrollToTop/compactLayout/normalLayout 翻译(5 种语言)

Inspired-By: https://github.com/algerkong/AlgerMusicPlayer/pull/589
2026-04-10 23:26:34 +08:00
Vanilla-puree 0ab784024c feat(download): 新增未保存下载设置时的确认对话框 (#507)
- feat(download): 关闭下载设置抽屉时检测未保存更改,提供取消/放弃/保存选项
- fix: 自动播放首次暂停无法暂停,移除不必要的 isFirstPlay 检查
- fix: 歌手详情路由添加 props key,修复跳转歌手详情不生效问题
- i18n: 添加 download.save.* 翻译(5 种语言)

Co-Authored-By: 心妄 <1661272893@qq.com>
2026-04-10 23:26:33 +08:00
alger ad2df12957 fix(core): 修复事件监听器泄漏
- App.vue: offline 监听器添加 onUnmounted 清理,移除冗余 console.log
- MusicHook.ts: document.onkeyup 直接赋值改为 addEventListener + 防重复
- MusicHook.ts: audio-ready 监听器提取为命名函数,先移除再注册防堆叠
2026-04-10 23:26:33 +08:00
alger a407045527 fix(player): 修复迷你模式恢复后歌词页面空白偏移
迷你播放栏的 togglePlaylist 设置 document.body.style.height='64px'
和 overflow='hidden',恢复主窗口时未清理,导致歌词 drawer 高度被限制。
在 mini-mode 事件处理中添加 body 样式重置。
2026-04-10 23:26:33 +08:00
alger 38723165a0 refactor(player): 提取播放栏共享逻辑为 composable
- 新增 useVolumeControl:统一音量管理(volumeSlider、mute、滚轮调节)
- 新增 useFavorite:收藏状态与切换
- 新增 usePlaybackControl:播放/暂停、上/下一首
- PlayBar、MiniPlayBar、SimplePlayBar、MobilePlayBar 使用新 composable
- 修复音量存储不一致:MiniPlayBar/SimplePlayBar 原先绕过 playerStore 直接操作 localStorage
2026-04-10 23:26:33 +08:00
alger 042b8ba6f8 fix(i18n): 补充 player.autoResumed/resumeFailed 翻译(5 种语言) 2026-03-29 13:20:45 +08:00
alger eb801cfbfd style(ui): 桌面端 message 毛玻璃样式,本地音乐页面全页滚动优化
- message 提示适配项目设计:全圆角、backdrop-blur、半透明背景、深色/浅色模式
- 本地音乐页面:hero 缩小可滚出、action bar 吸顶、歌曲列表跟随全页滚动
- 顺序播放到最后一首:用户点下一首保持播放仅提示,自然播完才停止
- i18n 新增 playListEnded(5 种语言)
2026-03-29 13:18:56 +08:00
alger 0cfec3dd82 refactor(player): 重构播放控制系统,移除 Howler.js 改用原生 HTMLAudioElement
- 新建 playbackController.ts,使用 generation-based 取消替代 playbackRequestManager 状态机
- audioService 重写:单一持久 HTMLAudioElement + Web Audio API,createMediaElementSource 只调一次
- playerCore 瘦身为纯状态管理,移除 handlePlayMusic/playAudio/checkPlaybackState
- playlist next/prev 简化,区分用户手动切歌和歌曲自然播完
- MusicHook 适配 HTMLAudioElement API(.currentTime/.duration/.paused)
- preloadService 从 Howl 实例缓存改为 URL 可用性验证
- 所有 view/component 调用者迁移到 playbackController.playTrack()

修复:快速切歌竞态、seek 到未缓冲位置失败、重启后自动播放循环提示、EQ 重建崩溃
2026-03-29 13:18:05 +08:00
alger 167f081ee6 fix(download): 下载中列表封面使用缩略图加速加载 2026-03-27 23:06:38 +08:00
alger c28368f783 fix(local-music): 扫描自动清理已删除文件,修复双滚动条
- scanFolders() 扫描时收集磁盘文件路径,完成后自动移除 IndexedDB 中已删除的条目
- 移除外层 n-scrollbar,改用 flex 布局,n-virtual-list 作为唯一滚动容器
2026-03-27 23:02:09 +08:00
alger bc46024499 refactor(download): 重构下载系统,支持暂停/恢复/取消,修复歌词加载
- 新建 DownloadManager 类(主进程),每个任务独立 AbortController 控制
- 新建 Pinia useDownloadStore 作为渲染进程单一数据源
- 支持暂停/恢复/取消下载,支持断点续传(Range header)
- 批量下载全部完成后发送汇总系统通知,单首不重复通知
- 并发数可配置(1-5),队列持久化(重启后恢复)
- 修复下载列表不全、封面加载失败、通知重复等 bug
- 修复本地/下载歌曲歌词加载:优先从 ID3/FLAC 元数据提取,API 作为 fallback
- 删除 useDownloadStatus.ts,统一状态管理
- DownloadDrawer/DownloadPage 全面重写,移除 @apply 违规
- 新增 5 语言 i18n 键值(暂停/恢复/取消/排队中等)
2026-03-27 23:02:08 +08:00
alger 59f71148af fix: 修复打包后白屏问题(createDiscreteApi 循环依赖)
playlist.ts 被 Vite 拆分为独立 chunk 后与主 chunk 形成循环依赖,
顶层调用 createDiscreteApi 导致 NMessageProvider 组件 TDZ 错误。
改为延迟初始化解决。同时移除调试代码。
2026-03-22 22:46:59 +08:00
alger b6d08b9660 fix: 扫码登录改为默认首选 & 更新 CHANGELOG v5.1.0 2026-03-22 19:13:34 +08:00
alger 2ef08412cf fix: 替换 NeteaseCloudMusicApi 为 netease-cloud-music-api-alger 2026-03-22 19:08:50 +08:00
alger 8e1dcd5c06 fix: 修复移动端全屏歌词前奏阶段第一句歌词不可见
getLrcStyle 在当前行无条件设置 color: transparent,
但前奏阶段 originalStyle 无 backgroundImage,导致文字透明不可见
2026-03-22 18:31:58 +08:00
alger 2b8378bbae feat: 重构心动模式与私人FM播放逻辑
- 心动模式从播放模式循环中独立,移至 SearchBar 作为独立按钮
- 新增私人FM自动续播:播放结束后自动获取下一首
- 播放列表设置时自动清除FM模式标志
- 顺序播放模式播放到最后一首后正确停止
- 新增获取关注歌手新歌 API
- 补充心动模式相关 i18n 翻译
2026-03-22 16:49:00 +08:00
alger 7f0b3c6469 fix: 设置桌面端最小窗口尺寸为 900x640 防止内容截断 2026-03-22 16:48:01 +08:00
alger 2f05663093 fix: 优化音乐列表页移动端按钮尺寸 2026-03-22 16:47:48 +08:00
alger 0ea3ac5b60 fix: 移除首页顶部多余 padding 2026-03-22 16:47:38 +08:00
alger bf3155b80a fix: HomeHero 快捷导航仅在移动端显示 2026-03-22 16:47:28 +08:00