Skip to content
This repository was archived by the owner on Jul 17, 2026. It is now read-only.

✨ feat: 新增本地缓存功能 - #640

Merged
imsyy merged 19 commits into
devfrom
dev-local
Dec 19, 2025
Merged

imsyy merged 19 commits into
devfrom
dev-local

Conversation

@imsyy

@imsyy imsyy commented Dec 18, 2025

Copy link
Copy Markdown
Member

No description provided.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @imsyy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求的核心目标是为应用程序引入全面的本地缓存功能,以优化音乐播放和本地文件管理的性能和用户体验。通过将缓存逻辑抽象为独立的Electron服务,并将其集成到播放器和歌词管理中,显著减少了对网络资源的依赖,并提高了本地音乐库扫描的效率。此外,还扩展了本地音乐的浏览方式,新增了按文件夹分类的视图。

Highlights

  • 本地缓存服务: 引入了全新的 CacheService 和 MusicCacheService,用于集中管理音乐和歌词的本地缓存。这包括缓存文件的写入、读取、删除、清空以及总大小计算,并支持根据大小限制自动清理旧缓存。
  • 本地音乐库扫描优化: 重构了本地音乐扫描逻辑,现在使用 LocalMusicService 进行管理,支持并发限制 (p-limit)、封面提取 (sharp),并在扫描过程中通过 IPC 流式传输进度和批量歌曲数据,显著提升了用户体验和性能。
  • 播放器缓存集成: 播放器现在会优先检查本地音乐缓存,减少网络请求。在线歌曲和解锁歌曲的 URL 都会尝试缓存到本地,并在网络请求失败时作为兜底方案。
  • 歌词缓存: 歌词管理模块现在支持缓存原始的在线歌词数据(LRC 和 TTML 格式),避免重复下载,加快歌词加载速度。
  • 新增本地音乐文件夹视图: 在本地音乐页面新增了按文件夹分类的视图,方便用户按目录浏览和管理本地歌曲。
  • 设置项与用户体验改进: 新增了缓存大小限制设置,并优化了播放器进度条悬浮提示的显示逻辑。同时,本地音乐路径管理更加灵活,移除了默认音乐路径的特殊处理。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

这次的 Pull Request 引入了一个全面的本地缓存功能,这是一个非常重要的改进。代码重构做得很好,将缓存逻辑拆分到专门的服务中(CacheService, MusicCacheService, LocalMusicService),大大改善了代码结构和关注点分离。新的本地音乐同步机制尤其出色,它采用了流式处理的方式向用户提供实时反馈,对于拥有大型音乐库的用户来说,这将极大地提升用户体验。此外,错误处理和回退机制(例如在离线时使用缓存音乐)也让播放器变得更加健壮。

尽管整体实现非常出色,我还是发现了一些可以改进的地方,主要涉及潜在的竞态条件和代码清晰度。具体细节请参考我的评论。

Comment on lines 109 to -111
) {
// 防止快速切歌
if (this._initializing) return;
this._initializing = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

这几行代码实现的状态锁被移除了,可能会在快速切歌时(连续多次调用 playSong)引发竞态条件。例如,当第一个 playSong 调用正在 await 时,第二个调用可能会进来并执行 audioManager.stop(),打断第一个调用的流程,导致状态不一致或播放错误的歌曲。建议恢复此状态锁或采用类似机制(如使用 p-limit 库限制并发为 1)来确保播放初始化的原子性,提升程序的稳定性。

Comment thread src/stores/data.ts
Comment on lines 242 to 259
async setNextPlaySong(song: SongType, index: number): Promise<number> {
// 获取原始数组副本
const currentList = toRaw(this.playList);
// 若为空,则直接添加
if (currentList.length === 0) {
const newList = [song];
this.playList = markRaw(newList);
await musicDB.setItem("playList", cloneDeep(newList));
if (this.playList.length === 0) {
this.playList = [song];
await musicDB.setItem("playList", cloneDeep(this.playList));
return 0;
}
// 浅拷贝数组,准备修改
let newList = [...currentList];
// 在当前播放位置之后插入歌曲
const indexAdd = index + 1;
// 插入
newList.splice(indexAdd, 0, song);
// 去重:移除除刚刚插入位置之外的相同 ID 歌曲
// 注意逻辑:保留 indexAdd 位置的,过滤掉其他位置重复的
newList = newList.filter((item, idx) => idx === indexAdd || item.id !== song.id);
this.playList = markRaw(newList);
await musicDB.setItem("playList", cloneDeep(toRaw(newList)));
this.playList.splice(indexAdd, 0, song);
// 移除重复的歌曲(如果存在)
const playList = this.playList.filter((item, idx) => idx === indexAdd || item.id !== song.id);
// 更新本地存储
this.playList = markRaw(playList);
await musicDB.setItem("playList", cloneDeep(playList));
// 返回刚刚插入的歌曲索引
return newList.findIndex((item) => item.id === song.id);
return playList.indexOf(song);
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

当前 setNextPlaySong 的实现直接在 this.playList 上使用了 splice 进行修改,然后又用 filter 创建了一个新数组。这种写法虽然能工作,但可读性稍差,因为 splice 是一个原地修改操作,紧接着又对同一个数组进行 filter 可能会让其他开发者感到困惑。

为了提高代码的清晰度和可维护性,建议先创建一个数组副本,在副本上执行所有操作,最后再将结果赋给 this.playList。

    async setNextPlaySong(song: SongType, index: number): Promise<number> {
      // 若为空,则直接添加
      if (this.playList.length === 0) {
        this.playList = [song];
        await musicDB.setItem("playList", cloneDeep(this.playList));
        return 0;
      }
      // 创建副本进行操作,避免直接修改 state
      const newList = [...this.playList];
      // 在当前播放位置之后插入歌曲
      const indexAdd = index + 1;
      newList.splice(indexAdd, 0, song);
      // 移除重复的歌曲(如果存在)
      const finalList = newList.filter((item, idx) => idx === indexAdd || item.id !== song.id);
      // 更新本地存储
      this.playList = markRaw(finalList);
      await musicDB.setItem("playList", cloneDeep(finalList));
      // 返回刚刚插入的歌曲索引
      return finalList.indexOf(song);
    },

Comment on lines +127 to +129
} catch (error: any) {
processLog.error("Download music cache failed:", error);
return { success: false, message: error.message };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在 catch 块中,将 error 类型断言为 any 并直接访问 error.message 是不安全的,因为 error 可能是任何类型的值(例如字符串或没有 message 属性的对象),这可能导致运行时错误。

建议将 error 类型声明为 unknown,并进行类型检查,以更安全地提取错误信息。

      } catch (error: unknown) {
        processLog.error("Download music cache failed:", error);
        const message = error instanceof Error ? error.message : String(error);
        return { success: false, message };
      }

@imsyy

imsyy commented Dec 19, 2025

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

这次的 Pull Request 是一次非常出色的重构和功能增强。将缓存逻辑抽象成独立的 CacheService、MusicCacheService 和 LocalMusicService 服务,极大地提升了代码的模块化、可维护性和可测试性,这是一个非常棒的架构改进。

本地音乐库的扫描性能得到了显著优化,通过引入持久化数据库、元数据缓存、并发控制(p-limit)以及流式数据传输,解决了扫描大量文件时可能出现的性能瓶颈和UI卡顿问题。同时,为在线音乐和歌词增加了缓存功能,能够有效减少网络请求,提升用户体验。

此外,播放逻辑的健壮性也得到了加强,例如使用 currentRequestToken 来处理切歌时的竞态条件,以及增加了防止因歌曲无法播放而无限切歌的机制。UI/UX 方面,新增的本地音乐“文件夹”视图和空状态的展示也让功能更加完善和友好。

总的来说,这是一次高质量的提交,代码考虑周全。我只发现了一个小问题,具体请看下面的评论。

menuActiveKey.value = routerName;
break;
}
console.log(menuActiveKey.value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这里有一个 console.log 语句,可能是调试时遗留下来的。建议在合并前移除,以保持控制台的整洁。

@imsyy
imsyy merged commit d34aead into dev Dec 19, 2025
3 checks passed
@imsyy
imsyy deleted the dev-local branch December 19, 2025 09:06
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant