Skip to content

Conversation

@kazukokawagawa
Copy link
Collaborator

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @kazukokawagawa, 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!

此拉取请求通过引入强大的多账号管理功能,显著提升了用户体验,允许用户在不同配置文件之间无缝切换。同时,它实现了一个全面的音质鉴权系统,根据用户的VIP订阅级别动态调整可用的播放和下载选项,确保符合服务层级并提供量身定制的体验。

Highlights

  • 多账号管理: 用户现在可以直接从用户菜单管理多个账号,包括切换、添加和移除现有账号。
  • 音质鉴权: 音频播放和下载选项现在根据用户的VIP状态(普通用户、VIP、SVIP)动态过滤,确保只提供授权的音质。
  • VIP状态检测: 增强了用户数据获取,以准确判断SVIP状态,从而影响可用的音质选项。
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.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

这个 Pull Request 引入了两个主要功能:基于 VIP 状态的音质鉴权和多账号管理。代码改动涉及面较广,实现得比较全面。基于 VIP 的音质过滤逻辑在多个组件中都得到了正确的实现,但在代码优化和类型安全方面还有一些提升空间,例如移除 @ts-ignoreas any。多账号切换功能设计良好,添加、切换和移除账号的流程清晰。我的审查主要关注于提升代码的清晰度、可维护性和类型安全。

Comment on lines 125 to 126
// @ts-ignore
allData = allData.filter((item) => allowedLevels.includes(item.level));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

应该避免使用 @ts-ignore,因为它会抑制潜在的类型错误。通过对 allowedLevels 进行类型断言,可以解决类型不匹配的问题,从而提高类型安全性和代码清晰度。

      allData = allData.filter((item) => (allowedLevels as readonly string[]).includes(item.level));

Comment on lines 130 to 131
// @ts-ignore
allData = allData.filter((item) => AUTHORIZED_QUALITY_LEVELS.NORMAL.includes(item.level));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这里也应该避免使用 @ts-ignore。可以通过对 AUTHORIZED_QUALITY_LEVELS.NORMAL 添加类型断言来修复类型错误。

    allData = allData.filter((item) => (AUTHORIZED_QUALITY_LEVELS.NORMAL as readonly string[]).includes(item.level));

Comment on lines 119 to 120
// @ts-ignore
allData = allData.filter((item) => allowedLevels.includes(item.level));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

建议避免使用 @ts-ignore。对 allowedLevels 进行类型断言可以解决类型错误并提高类型安全性。

Suggested change
// @ts-ignore
allData = allData.filter((item) => allowedLevels.includes(item.level));
allData = allData.filter((item) => (allowedLevels as readonly string[]).includes(item.level));

Comment on lines 124 to 125
// @ts-ignore
allData = allData.filter((item) => AUTHORIZED_QUALITY_LEVELS.NORMAL.includes(item.level));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

可以通过对 AUTHORIZED_QUALITY_LEVELS.NORMAL 添加类型断言来移除 @ts-ignore,这能让代码更安全、更清晰。

Suggested change
// @ts-ignore
allData = allData.filter((item) => AUTHORIZED_QUALITY_LEVELS.NORMAL.includes(item.level));
allData = allData.filter((item) => (AUTHORIZED_QUALITY_LEVELS.NORMAL as readonly string[]).includes(item.level));

Comment on lines 269 to 270
// @ts-ignore
options = options.filter((o) => allowedLevels.includes(o.value));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

使用 @ts-ignore 会隐藏潜在的类型问题。请对 allowedLevels 使用类型断言来正确修复类型错误。

Suggested change
// @ts-ignore
options = options.filter((o) => allowedLevels.includes(o.value));
options = options.filter((o) => (allowedLevels as readonly string[]).includes(o.value));

Comment on lines 274 to 275
// @ts-ignore
options = options.filter((o) => AUTHORIZED_QUALITY_LEVELS.NORMAL.includes(o.value));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

应该避免使用 @ts-ignore。对 AUTHORIZED_QUALITY_LEVELS.NORMAL 进行类型断言可以解决类型错误并提高类型安全性。

Suggested change
// @ts-ignore
options = options.filter((o) => AUTHORIZED_QUALITY_LEVELS.NORMAL.includes(o.value));
options = options.filter((o) => (AUTHORIZED_QUALITY_LEVELS.NORMAL as readonly string[]).includes(o.value));

Comment on lines 112 to 113
console.log("[QualityControl] Levels from API:", levels);
console.log("[QualityControl] User VIP Type:", dataStore.userData.vipType, "Login Status:", dataStore.userLoginStatus);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这几行 console.log 看起来是用于调试的。在合并到主分支之前最好将它们移除,以保持生产环境的控制台输出干净。

Comment on lines 116 to 137
if (dataStore.userLoginStatus) {
const vipType = dataStore.userData.vipType || 0;
// SVIP 拥有所有权限,不需要过滤
if (!isSvip(vipType)) {
const allowedLevels = isVip(vipType)
? AUTHORIZED_QUALITY_LEVELS.VIP
: AUTHORIZED_QUALITY_LEVELS.NORMAL;

statusStore.availableQualities = levels.filter((q) =>
(allowedLevels as readonly string[]).includes(q.level),
);
} else {
// SVIP
statusStore.availableQualities = levels;
}
} else {
// 未登录视同普通用户
const allowedLevels = AUTHORIZED_QUALITY_LEVELS.NORMAL;
statusStore.availableQualities = levels.filter((q) =>
(allowedLevels as readonly string[]).includes(q.level),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

基于 VIP 状态过滤可用音质的逻辑可以被简化,使其更简洁。当前实现存在一些冗余,比如针对 SVIP 用户的 else 分支。重构这部分代码可以提高可读性和可维护性。

        const vipType = dataStore.userLoginStatus ? dataStore.userData.vipType || 0 : 0;
        if (isSvip(vipType)) {
          statusStore.availableQualities = levels;
        } else {
          const allowedLevels = isVip(vipType)
            ? AUTHORIZED_QUALITY_LEVELS.VIP
            : AUTHORIZED_QUALITY_LEVELS.NORMAL;
          statusStore.availableQualities = levels.filter((q) =>
            (allowedLevels as readonly string[]).includes(q.level),
          );
        }

if (!(allowedLevels as readonly string[]).includes(level)) {
console.warn(`⚠️ [${id}] 用户无权播放 ${level},降级至最高可用音质`);
// 降级策略: 使用允许列表中的最后一个(通常是最高质量)
level = allowedLevels[allowedLevels.length - 1] as any;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

使用 as any 会破坏 TypeScript 类型系统的作用。最好使用更具体的类型断言。level 变量的类型可以更精确,例如使用 typeof settingStore.songLevel

Suggested change
level = allowedLevels[allowedLevels.length - 1] as any;
level = allowedLevels[allowedLevels.length - 1] as typeof settingStore.songLevel;

@kazukokawagawa
Copy link
Collaborator Author

/gemini review

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

这个 PR 主要实现了音质鉴权和多账号管理两大功能,并新增了音乐百科页面。代码整体结构清晰,功能实现完整。
我发现了一些可以改进的地方,主要包括:

  • 部分代码存在重复,可以提取为公共函数以提高可维护性。
  • 代码中存在一些用于调试的 console.log,在合并前应移除。
  • 部分逻辑可以重构得更简洁。
  • 一些硬编码的魔法数字可以定义为常量,以提高代码可读性。

具体建议请看下面的评论。

Comment on lines 118 to 130
// 根据 VIP 状态过滤
if (dataStore.userLoginStatus) {
const vipType = dataStore.userData.vipType || 0;
if (!isSvip(vipType)) {
const allowedLevels = isVip(vipType)
? AUTHORIZED_QUALITY_LEVELS.VIP
: AUTHORIZED_QUALITY_LEVELS.NORMAL;
allData = allData.filter((item) => (allowedLevels as readonly string[]).includes(item.level));
}
} else {
// 未登录
allData = allData.filter((item) => (AUTHORIZED_QUALITY_LEVELS.NORMAL as readonly string[]).includes(item.level));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

基于 VIP 状态过滤音质选项的逻辑在多个文件(DownloadModal.vue, local.ts, play.ts)中重复出现。这使得代码难以维护,因为对该逻辑的任何更改都需要在所有地方同步应用。最好的做法是将此逻辑提取到一个共享的工具函数中。例如,你可以在一个 composable 或工具文件中创建一个函数 getAuthorizedQualityOptions(allData, userLoginStatus, vipType) 并复用它。

</n-tag>
<!-- 脏标 -->
<n-tag
v-if="settingStore.showSongExplicitTag && (song.mark && (song.mark & 1048576))"
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这里的魔法数字 1048576 用于检查歌曲的“脏标”标记。建议将其定义为一个具有描述性名称的常量(例如 EXPLICIT_CONTENT_MARK),并放在一个共享的工具或常量文件中,例如 src/utils/meta.ts。这能提高代码的可读性和可维护性。例如:export const EXPLICIT_CONTENT_MARK = 1048576;

Comment on lines 205 to 207

userMenuShow.value = false;

Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这里有不必要的空行,可以移除以使代码更紧凑。

  saveCurrentAccount();
  userMenuShow.value = false;
  // 打开登录框 (强制模式, 不登出当前用户以保持 cookies 直到新登录成功)

Comment on lines 112 to 113
console.log("[QualityControl] Levels from API:", levels);
console.log("[QualityControl] User VIP Type:", dataStore.userData.vipType, "Login Status:", dataStore.userLoginStatus);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这两行 console.log 看起来是用于调试的,在合并到主分支前应该移除。

Comment on lines 115 to 137
// 根据 VIP 类型过滤音质
if (dataStore.userLoginStatus) {
const vipType = dataStore.userData.vipType || 0;
// SVIP 拥有所有权限,不需要过滤
if (!isSvip(vipType)) {
const allowedLevels = isVip(vipType)
? AUTHORIZED_QUALITY_LEVELS.VIP
: AUTHORIZED_QUALITY_LEVELS.NORMAL;

statusStore.availableQualities = levels.filter((q) =>
(allowedLevels as readonly string[]).includes(q.level),
);
} else {
// SVIP
statusStore.availableQualities = levels;
}
} else {
// 未登录视同普通用户
const allowedLevels = AUTHORIZED_QUALITY_LEVELS.NORMAL;
statusStore.availableQualities = levels.filter((q) =>
(allowedLevels as readonly string[]).includes(q.level),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

基于 VIP 状态过滤可用音质的逻辑有些嵌套,可以简化以提高可读性。可以先根据用户状态确定 allowedLevels,然后应用一次过滤。

        // 根据 VIP 类型过滤音质
        const vipType = dataStore.userLoginStatus ? (dataStore.userData.vipType || 0) : 0;
        if (isSvip(vipType)) {
          statusStore.availableQualities = levels;
        } else {
          const allowedLevels = isVip(vipType)
            ? AUTHORIZED_QUALITY_LEVELS.VIP
            : AUTHORIZED_QUALITY_LEVELS.NORMAL;
          statusStore.availableQualities = levels.filter((q) =>
            (allowedLevels as readonly string[]).includes(q.level),
          );
        }


// 如果请求的音质不在允许列表中
if (!(allowedLevels as readonly string[]).includes(level)) {
console.warn(`⚠️ [${id}] 用户无权播放 ${level},降级至最高可用音质`);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这个 console.warn 看起来是用于调试的,在合并前应该移除。生产环境的代码通常不应包含这类控制台输出。


// 校验:如果必须信息缺失,不保存
if (!userId || !name || name === "未知用户名") {
console.warn("User data incomplete, skipping save");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这个 console.warn 看起来是用于调试的,应该移除。

Comment on lines 73 to 86
/** 检查是否为 SVIP */
export const isSvip = (vipType: number) => {
// 11 is standard SVIP code
if (vipType === VIP_LEVELS.SVIP) return true;
// Check enriched data from store
const dataStore = useDataStore();
return !!dataStore.userData.isSvip;
};

/** 检查是否为 VIP (包括 SVIP) */
export const isVip = (vipType: number) => {
const vipCodes: number[] = [VIP_LEVELS.VIP, VIP_LEVELS.VIP_ANNUAL];
return vipCodes.includes(vipType) || isSvip(vipType);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

isSvipisVip 函数依赖了 useDataStore,这使得 meta.ts 文件变得不纯粹,并与应用的状态管理紧密耦合。通常来说,最好保持像 meta.ts 这样的工具文件只包含静态数据和纯函数。建议将这些函数移动到更合适的位置,比如 src/utils/auth.ts,因为它已经处理了用户状态相关的逻辑。这会改善代码的组织结构和关注点分离。

<template v-if="Array.isArray(songInfo.artists)">
<span v-for="(ar, index) in songInfo.artists" :key="ar.id">
<n-text>{{ ar.name }}</n-text>
<span v-if="Number(index) < songInfo.artists.length - 1"> / </span>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

v-for 指令提供的 index 已经是一个数字类型,Number(index) 的转换是多余的,可以移除。

                  <span v-if="index < songInfo.artists.length - 1"> / </span>

Comment on lines 805 to 810
white-space: normal !important;
overflow: visible !important;
display: block !important;
-webkit-line-clamp: unset !important;
line-clamp: unset !important;
height: auto !important;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

这里使用的 !important 应该尽可能避免。它会破坏 CSS 的自然级联,使样式更难维护和调试。更好的方法是增加选择器的特异性来覆盖 text-hidden 类的样式。例如,你可以使用更具体的选择器,如 .res-info .res-title

                          white-space: normal;
                          overflow: visible;
                          display: block;
                          -webkit-line-clamp: unset;
                          line-clamp: unset;
                          height: auto;

gemini-code-assist[bot]

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants