diff --git a/README.MD b/README.MD
index 0ace74d..3044fc4 100644
--- a/README.MD
+++ b/README.MD
@@ -7,6 +7,7 @@
- Unlock Music 的 CLI 版本可以在 [unlock-music/cli] 找到,大批量转换建议使用 CLI 版本。
- 我们新建了 Telegram 群组 [`@unlock_music_chat`] ,欢迎加入!
- CI 自动构建已经部署,可以在 [Packages][um-react-packages] 下载。
+- [常见问题参考](./docs/faq_zh-hans.md)
[授权协议]: https://git.unlock-music.dev/um/um-react/src/branch/main/LICENSE
[unlock-music/cli]: https://git.unlock-music.dev/um/cli
diff --git a/docs/assets/faq_1_home.webp b/docs/assets/faq_1_home.webp
new file mode 100644
index 0000000..98aa939
Binary files /dev/null and b/docs/assets/faq_1_home.webp differ
diff --git a/docs/assets/faq_2_import.webp b/docs/assets/faq_2_import.webp
new file mode 100644
index 0000000..0ab5984
Binary files /dev/null and b/docs/assets/faq_2_import.webp differ
diff --git a/docs/assets/faq_3_instructions.webp b/docs/assets/faq_3_instructions.webp
new file mode 100644
index 0000000..9431e13
Binary files /dev/null and b/docs/assets/faq_3_instructions.webp differ
diff --git a/docs/faq_zh-hans.md b/docs/faq_zh-hans.md
index ede9a4f..8074a14 100644
--- a/docs/faq_zh-hans.md
+++ b/docs/faq_zh-hans.md
@@ -1,58 +1,72 @@
-# 常见问题解答
-
-## QQ音乐
-### 解锁失败
-#### 1、请检查您的文件。
-尝试用下载音乐的设备播放一次看看,如果QQ音乐都没法播放,那解锁肯定会受到影响哦。
-#### 2、检查您的平台。
-日前,仅Windows客户端下载的歌曲无需密钥,其余平台的官方正式版本均需要提取密钥。
-
->iOS用户提取歌曲困难,建议换用电脑操作;Android用户提取密钥需要root,也建议用电脑操作。
-
->重复下载同一首的歌曲**不重复扣下载配额**,但是*同一首歌的两个版本会重复扣下载配额*,请仔细分辨。
-
-提取密钥教程请访问[新版解锁网站](https://um-react.netlify.app/),前往网站内的设置→“添加一条密钥”旁的**下拉按钮**→从文件导入密钥…→选择您对应的平台查看具体教程。
->如果仍无法理解,可参考文末的图片操作
-
-## 酷我音乐
-### 解锁失败
-酷我音乐的新版加密需要导入密钥。
-#### 1、请检查您的文件。
-尝试用下载音乐的设备播放一次看看,如果酷我音乐都没法播放,那解锁肯定会受到影响哦。
-#### 2、检查您的平台。
-日前,仅手机客户端下载的歌曲**至臻全景声**及**至臻母带**为新版加密,手机平台的其他音质暂时不需要提取密钥,PC平台暂未推出使用新版加密的音质。
-
->iOS用户提取歌曲需要;Android用户提取密钥需要root,或者注入文件提供器。
-
-提取密钥教程请访问[新版解锁网站](https://um-react.netlify.app/),前往网站内的设置→切换密钥为KWMv2密钥→“添加一条密钥”旁的**下拉按钮**→从文件导入密钥…→选择您对应的平台查看具体教程。
-
->图片教程请参考QQ音乐(在文末),酷我音乐仅仅是需要切换一下密钥类型。
-## 网易云音乐
-### 解锁失败
-您大概率正在使用Windows平台的网易云音乐3.0测试版。该版本对歌曲的信息新增了某些字段,导致旧版解锁识别错误。您可以找1.10.5版本的旧解锁网站,或者直接换[新版解锁网站](https://um-react.netlify.app/)。
->[旧解锁网站Demo](https://demo.unlock-music.dev/)拥有者暂时联系不上,所以暂时无法更新。
-
-## 其他问题
-### 新版解锁网站解锁的歌曲没有封面
-目前新版没有做歌曲信息匹配与编辑,所以歌曲如果自己没有写入歌曲信息,解出来就是没有的。
-### 新版解锁网站没有批量下载
-目前没有做。抱歉。
-
-## 仍有问题?
-欢迎进入[Telegram交流群](https://t.me/unlock_music_chat),一起探讨。
-
->QQ音乐导入密钥的图片教程
-
-
-
-
点击设置
-
-
-
-
点击导入
-
-
-
-
查看密钥教程
-
-
\ No newline at end of file
+# 常见问题解答
+
+## QQ 音乐
+
+### 解锁失败
+
+#### 1、请检查您的文件。
+
+尝试用下载音乐的设备播放一次看看,如果 QQ 音乐都没法播放,那解锁肯定会受到影响哦。
+
+#### 2、检查您的平台。
+
+日前,仅 Windows 客户端下载的歌曲无需密钥,其余平台的官方正式版本均需要提取密钥。
+
+> iOS 用户提取歌曲困难,建议换用电脑操作;Android 用户提取密钥需要 root,也建议用电脑操作。
+
+> 重复下载同一首的歌曲**不重复扣下载配额**,但是*同一首歌的两个版本会重复扣下载配额*,请仔细分辨。
+
+提取密钥教程请访问[新版解锁网站](https://um-react.netlify.app/),前往网站内的设置 →“添加一条密钥”旁的**下拉按钮**→ 从文件导入密钥…→ 选择您对应的平台查看具体教程。
+
+> 如果仍无法理解,可参考文末的图片操作
+
+## 酷我音乐
+
+### 解锁失败
+
+酷我音乐的新版加密需要导入密钥。
+
+#### 1、请检查您的文件。
+
+尝试用下载音乐的设备播放一次看看,如果酷我音乐都没法播放,那解锁肯定会受到影响哦。
+
+#### 2、检查您的平台。
+
+日前,仅手机客户端下载的歌曲**至臻全景声**及**至臻母带**为新版加密,手机平台的其他音质暂时不需要提取密钥,PC 平台暂未推出使用新版加密的音质。
+
+> Android 用户提取密钥需要 root,或者注入文件提供器。
+
+提取密钥教程请访问[新版解锁网站](https://um-react.netlify.app/),前往网站内的设置 →切换密钥为 KWMv2 密钥→“添加一条密钥”旁的**下拉按钮**→ 从文件导入密钥…→ 选择您对应的平台查看具体教程。
+
+> 图片教程请参考 QQ 音乐(在文末),酷我音乐仅仅是需要切换一下密钥类型。
+
+## 网易云音乐
+
+### 解锁失败
+
+您大概率正在使用 Windows 平台的网易云音乐 3.0 测试版。该版本对歌曲的信息新增了某些字段,导致旧版解锁识别错误。您可以找 1.10.5 版本的旧解锁网站,或者直接换[新版解锁网站](https://um-react.netlify.app/)。
+
+> [旧解锁网站 Demo](https://demo.unlock-music.dev/)拥有者暂时联系不上,所以暂时无法更新。
+
+## 其他问题
+
+### 新版解锁网站解锁的歌曲没有封面
+
+目前新版没有做歌曲信息匹配与编辑,所以歌曲如果自己没有写入歌曲信息,解出来就是没有的。
+
+### 新版解锁网站没有批量下载
+
+目前没有做。抱歉。
+
+## 仍有问题?
+
+欢迎进入[Telegram 交流群](https://t.me/unlock_music_chat),一起探讨。
+
+> QQ 音乐导入密钥的图片教程
+
+1. 选择【设定】
+
![选择【设定】](./assets/faq_1_home.webp)
+2. 点击下拉菜单,选择【从文件导入密钥…】
+
![点击下拉菜单,选择【从文件导入密钥…】](./assets/faq_2_import.webp)
+3. 选择对应的客户端并查阅说明
+
![选择对应的客户端并查阅说明](./assets/faq_3_instructions.webp)
diff --git a/src/components/AppRoot.tsx b/src/components/AppRoot.tsx
index e205487..a457092 100644
--- a/src/components/AppRoot.tsx
+++ b/src/components/AppRoot.tsx
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
-import { MdSettings, MdHome } from 'react-icons/md';
+import { MdSettings, MdHome, MdQuestionAnswer } from 'react-icons/md';
import { ChakraProvider, Tabs, TabList, TabPanels, Tab, TabPanel, Icon, chakra } from '@chakra-ui/react';
import { MainTab } from '~/tabs/MainTab';
@@ -10,6 +10,7 @@ import { theme } from '~/theme';
import { persistSettings } from '~/features/settings/persistSettings';
import { setupStore } from '~/store';
import { Footer } from '~/components/Footer';
+import { FaqTab } from '~/tabs/FaqTab';
// Private to this file only.
const store = setupStore();
@@ -30,6 +31,10 @@ export function AppRoot() {
设置
+
+
+ 问答
+
@@ -39,6 +44,9 @@ export function AppRoot() {
+
+
+
diff --git a/src/components/HelpText/Header3.tsx b/src/components/HelpText/Header3.tsx
new file mode 100644
index 0000000..67e1caf
--- /dev/null
+++ b/src/components/HelpText/Header3.tsx
@@ -0,0 +1,26 @@
+import { Heading } from '@chakra-ui/react';
+import React from 'react';
+
+export interface Header3Props {
+ children: React.ReactNode;
+ id?: string;
+ className?: string;
+}
+
+export function Header3({ children, className, id }: Header3Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/HelpText/Header4.tsx b/src/components/HelpText/Header4.tsx
new file mode 100644
index 0000000..f2b8deb
--- /dev/null
+++ b/src/components/HelpText/Header4.tsx
@@ -0,0 +1,16 @@
+import { Heading } from '@chakra-ui/react';
+import React from 'react';
+
+export interface Header4Props {
+ children: React.ReactNode;
+ id?: string;
+ className?: string;
+}
+
+export function Header4({ children, className, id }: Header4Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/HelpText/HiWord.tsx b/src/components/HelpText/HiWord.tsx
new file mode 100644
index 0000000..54bee0f
--- /dev/null
+++ b/src/components/HelpText/HiWord.tsx
@@ -0,0 +1,9 @@
+import { Mark } from '@chakra-ui/react';
+
+export function HiWord({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/HelpText/VQuote.tsx b/src/components/HelpText/VQuote.tsx
new file mode 100644
index 0000000..274637c
--- /dev/null
+++ b/src/components/HelpText/VQuote.tsx
@@ -0,0 +1,13 @@
+import { chakra, css } from '@chakra-ui/react';
+
+const cssUnselectable = css({ pointerEvents: 'none', userSelect: 'none' });
+
+export function VQuote({ children }: { children: React.ReactNode }) {
+ return (
+ <>
+ 「
+ {children}
+ 」
+ >
+ );
+}
diff --git a/src/components/ProjectIssue.tsx b/src/components/ProjectIssue.tsx
new file mode 100644
index 0000000..e56055a
--- /dev/null
+++ b/src/components/ProjectIssue.tsx
@@ -0,0 +1,17 @@
+export interface ProjectIssueProps {
+ id: number | string;
+ title?: string;
+}
+
+export function ProjectIssue({ id, title }: ProjectIssueProps) {
+ return (
+
+ {`#${id}`}
+ {title && ` - ${title}`}
+
+ );
+}
diff --git a/src/faq/KuwoFAQ.tsx b/src/faq/KuwoFAQ.tsx
new file mode 100644
index 0000000..c955de1
--- /dev/null
+++ b/src/faq/KuwoFAQ.tsx
@@ -0,0 +1,50 @@
+import { Alert, AlertIcon, Container, List, ListItem, Text, VStack, chakra } from '@chakra-ui/react';
+import { Header4 } from '~/components/HelpText/Header4';
+import { VQuote } from '~/components/HelpText/VQuote';
+import { SegmentTryOfficialPlayer } from './SegmentTryOfficialPlayer';
+import { HiWord } from '~/components/HelpText/HiWord';
+import { KWMv2AllInstructions } from '~/features/settings/panels/KWMv2/KWMv2AllInstructions';
+import { SegmentKeyImportInstructions } from './SegmentKeyImportInstructions';
+
+export function KuwoFAQ() {
+ return (
+ <>
+ 解锁失败
+
+
+
+
+
+
+ 2、检查您的平台
+
+
+ 日前,仅手机客户端下载的
+
+ 至臻全景声
+
+ 及
+
+ 至臻母带
+
+ {'音质的音乐文件采用新版加密。'}
+
+ 其他音质目前不需要提取密钥。
+ PC平台暂未推出使用新版加密的音质。
+
+
+
+
+
+ Android 用户提取密钥需要 root 权限,或注入文件提供器。
+ 请注意:项目组不提倡使用第三方修改版应用亦不会提供,使用前请自行评估风险。
+
+
+
+
+ } />
+
+
+ >
+ );
+}
diff --git a/src/faq/OtherFAQ.tsx b/src/faq/OtherFAQ.tsx
new file mode 100644
index 0000000..f044698
--- /dev/null
+++ b/src/faq/OtherFAQ.tsx
@@ -0,0 +1,28 @@
+import { ExternalLinkIcon } from '@chakra-ui/icons';
+import { Link, Text } from '@chakra-ui/react';
+import { Header4 } from '~/components/HelpText/Header4';
+import { ProjectIssue } from '~/components/ProjectIssue';
+
+export function OtherFAQ() {
+ return (
+ <>
+ 解密后没有封面等信息
+ 该项目进行解密处理。如果加密前的资源没有内嵌元信息或封面,解密的文件也没有。
+ 请使用第三方工具进行编辑或管理元信息。
+ 如何批量下载
+
+ 暂时没有实现,不过你可以在 以及{' '}
+ 追踪该问题。
+
+ 有更多问题?
+
+ {'欢迎进入 '}
+
+ Telegram “音乐解锁-交流” 交流群
+
+
+ {' 一起探讨。'}
+
+ >
+ );
+}
diff --git a/src/faq/QQMusicFAQ.tsx b/src/faq/QQMusicFAQ.tsx
new file mode 100644
index 0000000..33d3bc8
--- /dev/null
+++ b/src/faq/QQMusicFAQ.tsx
@@ -0,0 +1,40 @@
+import { Alert, AlertIcon, Container, List, ListItem, Text, chakra } from '@chakra-ui/react';
+import { Header4 } from '~/components/HelpText/Header4';
+import { SegmentTryOfficialPlayer } from './SegmentTryOfficialPlayer';
+import { QMCv2AllInstructions } from '~/features/settings/panels/QMCv2/QMCv2AllInstructions';
+import { SegmentKeyImportInstructions } from './SegmentKeyImportInstructions';
+
+export function QQMusicFAQ() {
+ return (
+ <>
+ 解锁失败
+
+
+
+
+
+
+ 2、检查您的平台
+
+ 日前,仅Windows客户端下载的歌曲无需密钥,其余平台的官方正式版本均需要提取密钥。
+
+
+
+
+ iOS用户提取歌曲困难,建议换用电脑操作;Android用户提取密钥需要root,也建议用电脑操作。
+
+
+
+
+
+
+ 重复下载同一首的歌曲不重复扣下载配额,但是同一首歌的两个版本会重复扣下载配额,请仔细分辨。
+
+
+
+ } />
+
+
+ >
+ );
+}
diff --git a/src/faq/SegmentAddKeyDropdown.tsx b/src/faq/SegmentAddKeyDropdown.tsx
new file mode 100644
index 0000000..c529e82
--- /dev/null
+++ b/src/faq/SegmentAddKeyDropdown.tsx
@@ -0,0 +1,25 @@
+import { Flex, IconButton } from '@chakra-ui/react';
+import { MdExpandMore } from 'react-icons/md';
+import { HiWord } from '~/components/HelpText/HiWord';
+import { VQuote } from '~/components/HelpText/VQuote';
+
+export function SegmentAddKeyDropdown() {
+ return (
+
+ 按下添加一条密钥按钮
+ 右侧的
+ }
+ ml="2"
+ borderTopLeftRadius={0}
+ borderBottomLeftRadius={0}
+ isDisabled
+ css={{ ':disabled': { opacity: 1 } }}
+ aria-label="示例按钮"
+ />
+
+ );
+}
diff --git a/src/faq/SegmentKeyImportInstructions.tsx b/src/faq/SegmentKeyImportInstructions.tsx
new file mode 100644
index 0000000..8d35f47
--- /dev/null
+++ b/src/faq/SegmentKeyImportInstructions.tsx
@@ -0,0 +1,44 @@
+import { Flex, Icon, ListItem, OrderedList, Tabs, Text } from '@chakra-ui/react';
+import { SegmentTopNavSettings } from './SegmentTopNavSettings';
+import { VQuote } from '~/components/HelpText/VQuote';
+import { SegmentAddKeyDropdown } from './SegmentAddKeyDropdown';
+import React from 'react';
+import { MdFileUpload } from 'react-icons/md';
+
+export interface SegmentKeyImportInstructionsProps {
+ clientInstructions: React.ReactNode;
+ tab: string;
+}
+
+export function SegmentKeyImportInstructions({ clientInstructions, tab }: SegmentKeyImportInstructionsProps) {
+ return (
+ <>
+ 导入密钥可以参考下面的步骤:
+
+
+
+
+
+ 设定区域选择{tab}
+
+
+
+
+
+
+ {'选择 '}
+
+ 从文件导入密钥…
+
+
+
+
+ 选择你的客户端平台来查看密钥提取说明:
+
+ {clientInstructions}
+
+
+
+ >
+ );
+}
diff --git a/src/faq/SegmentTopNavSettings.tsx b/src/faq/SegmentTopNavSettings.tsx
new file mode 100644
index 0000000..918ab6e
--- /dev/null
+++ b/src/faq/SegmentTopNavSettings.tsx
@@ -0,0 +1,9 @@
+import { VQuote } from '~/components/HelpText/VQuote';
+
+export function SegmentTopNavSettings() {
+ return (
+ <>
+ 点击顶部的设置
+ >
+ );
+}
diff --git a/src/faq/SegmentTryOfficialPlayer.tsx b/src/faq/SegmentTryOfficialPlayer.tsx
new file mode 100644
index 0000000..3142621
--- /dev/null
+++ b/src/faq/SegmentTryOfficialPlayer.tsx
@@ -0,0 +1,12 @@
+import { Text, chakra } from '@chakra-ui/react';
+
+export function SegmentTryOfficialPlayer() {
+ return (
+ <>
+
+ 1、请检查您的文件
+
+ 尝试用下载音乐的设备播放一次看看,如果官方客户端都无法播放,那解锁肯定会失败哦。
+ >
+ );
+}
diff --git a/src/features/settings/panels/KWMv2/KWMv2AllInstructions.tsx b/src/features/settings/panels/KWMv2/KWMv2AllInstructions.tsx
new file mode 100644
index 0000000..144e604
--- /dev/null
+++ b/src/features/settings/panels/KWMv2/KWMv2AllInstructions.tsx
@@ -0,0 +1,25 @@
+import { Tab, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
+import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
+import { InstructionsPC } from './InstructionsPC';
+
+export function KWMv2AllInstructions() {
+ return (
+ <>
+
+ 安卓
+ Windows
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/features/settings/panels/PanelKWMv2Key.tsx b/src/features/settings/panels/PanelKWMv2Key.tsx
index ba6d4e7..ca97311 100644
--- a/src/features/settings/panels/PanelKWMv2Key.tsx
+++ b/src/features/settings/panels/PanelKWMv2Key.tsx
@@ -14,10 +14,6 @@ import {
MenuDivider,
MenuItem,
MenuList,
- Tab,
- TabList,
- TabPanel,
- TabPanels,
Text,
useToast,
} from '@chakra-ui/react';
@@ -32,8 +28,7 @@ import { kwm2AddKey, kwm2ClearKeys, kwm2ImportKeys } from '../settingsSlice';
import { selectStagingKWMv2Keys } from '../settingsSelector';
import { KWMv2EKeyItem } from './KWMv2/KWMv2EKeyItem';
import type { StagingKWMv2Key } from '../keyFormats';
-import { InstructionsPC } from './KWMv2/InstructionsPC';
-import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
+import { KWMv2AllInstructions } from './KWMv2/KWMv2AllInstructions';
export function PanelKWMv2Key() {
const toast = useToast();
@@ -120,21 +115,7 @@ export function PanelKWMv2Key() {
onClose={() => setShowImportModal(false)}
onImport={handleSecretImport}
>
-
- 安卓
- Windows
-
-
-
-
-
-
-
-
-
+
);
diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx
index c309b2c..802eac7 100644
--- a/src/features/settings/panels/PanelQMCv2Key.tsx
+++ b/src/features/settings/panels/PanelQMCv2Key.tsx
@@ -14,10 +14,6 @@ import {
MenuDivider,
MenuItem,
MenuList,
- Tab,
- TabList,
- TabPanel,
- TabPanels,
Text,
Tooltip,
useToast,
@@ -34,10 +30,7 @@ import { StagingQMCv2Key } from '../keyFormats';
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
import { MMKVParser } from '~/util/MMKVParser';
import { getFileName } from '~/util/pathHelper';
-import { InstructionsIOS } from './QMCv2/InstructionsIOS';
-import { InstructionsMac } from './QMCv2/InstructionsMac';
-import { InstructionsPC } from './QMCv2/InstructionsPC';
-import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
+import { QMCv2AllInstructions } from './QMCv2/QMCv2AllInstructions';
export function PanelQMCv2Key() {
const toast = useToast();
@@ -171,26 +164,7 @@ export function PanelQMCv2Key() {
onClose={() => setShowImportModal(false)}
onImport={handleSecretImport}
>
-
- 安卓
- iOS
- Mac
- Windows
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
);
diff --git a/src/features/settings/panels/QMCv2/QMCv2AllInstructions.tsx b/src/features/settings/panels/QMCv2/QMCv2AllInstructions.tsx
new file mode 100644
index 0000000..3cfd9f5
--- /dev/null
+++ b/src/features/settings/panels/QMCv2/QMCv2AllInstructions.tsx
@@ -0,0 +1,32 @@
+import { Tab, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
+import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
+import { InstructionsIOS } from './InstructionsIOS';
+import { InstructionsMac } from './InstructionsMac';
+import { InstructionsPC } from './InstructionsPC';
+
+export function QMCv2AllInstructions() {
+ return (
+ <>
+
+ 安卓
+ iOS
+ Mac
+ Windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/tabs/FaqTab.tsx b/src/tabs/FaqTab.tsx
new file mode 100644
index 0000000..2ac9c3a
--- /dev/null
+++ b/src/tabs/FaqTab.tsx
@@ -0,0 +1,21 @@
+import { Center, Container, Heading } from '@chakra-ui/react';
+import { Header3 } from '~/components/HelpText/Header3';
+import { KuwoFAQ } from '~/faq/KuwoFAQ';
+import { OtherFAQ } from '~/faq/OtherFAQ';
+import { QQMusicFAQ } from '~/faq/QQMusicFAQ';
+
+export function FaqTab() {
+ return (
+
+
+ 常见问题解答
+
+ QQ 音乐
+
+ 酷我音乐
+
+ 其它问题
+
+
+ );
+}