Compare commits
No commits in common. "e956fb60adb875571a0bff2d5f3ccebbfe734fd7" and "da39d5f5c1121ea5e34f2722a95f56a5a78c2ca9" have entirely different histories.
e956fb60ad
...
da39d5f5c1
@ -31,9 +31,10 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-icons": "^4.9.0",
|
"react-icons": "^4.9.0",
|
||||||
|
"react-markdown": "^8.0.7",
|
||||||
"react-promise-suspense": "^0.3.4",
|
"react-promise-suspense": "^0.3.4",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^8.0.5",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"remark-gfm": "^3.0.1",
|
||||||
"sass": "^1.63.3",
|
"sass": "^1.63.3",
|
||||||
"sql.js": "^1.8.0"
|
"sql.js": "^1.8.0"
|
||||||
},
|
},
|
||||||
@ -45,7 +46,6 @@
|
|||||||
"@types/node": "^20.2.5",
|
"@types/node": "^20.2.5",
|
||||||
"@types/react": "^18.2.7",
|
"@types/react": "^18.2.7",
|
||||||
"@types/react-dom": "^18.2.4",
|
"@types/react-dom": "^18.2.4",
|
||||||
"@types/react-syntax-highlighter": "^15.5.7",
|
|
||||||
"@types/sql.js": "^1.4.4",
|
"@types/sql.js": "^1.4.4",
|
||||||
"@types/testing-library__jest-dom": "^5.14.6",
|
"@types/testing-library__jest-dom": "^5.14.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
|
2482
pnpm-lock.yaml
2482
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
26
src/components/MarkdownContent.module.scss
Normal file
26
src/components/MarkdownContent.module.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.markdown {
|
||||||
|
h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.15em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
pre {
|
||||||
|
margin-block-start: 1em;
|
||||||
|
margin-block-end: 1em;
|
||||||
|
margin-inline-start: 0px;
|
||||||
|
margin-inline-end: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
border-left: 2px solid #ddd;
|
||||||
|
}
|
||||||
|
}
|
11
src/components/MarkdownContent.tsx
Normal file
11
src/components/MarkdownContent.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import MarkdownContentClass from './MarkdownContent.module.scss';
|
||||||
|
|
||||||
|
export function MarkdownContent({ children }: { children: string }) {
|
||||||
|
return (
|
||||||
|
<div className={MarkdownContentClass.markdown}>
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{children}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
23
src/features/settings/panels/QMCv2/DocAndroid.md
Normal file
23
src/features/settings/panels/QMCv2/DocAndroid.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
### 安卓端获取密钥数据库
|
||||||
|
|
||||||
|
你可能需要 root 或类似的访问权限。绝大多数情况下,这会导致你的安卓设备失去保修资格。
|
||||||
|
|
||||||
|
#### 提取文件(文件浏览器)
|
||||||
|
|
||||||
|
1. 提升到 `root` 权限,访问 `/data/data/com.tencent.qqmusic/databases/` 目录,将文件 `player_process_db`
|
||||||
|
复制到正常模式下用户可访问的目录(如下载目录)。
|
||||||
|
2. 如果你需要在电脑上进行解密操作,请将该文件复制到电脑。
|
||||||
|
3. 选择该文件。
|
||||||
|
|
||||||
|
#### 提取文件(ADB)
|
||||||
|
|
||||||
|
※ 目前该指令只支持 Linux & Mac
|
||||||
|
|
||||||
|
1. 打开终端并安装好依赖,并复制下述指令:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
adb shell su -c "cat '/data/data/com.tencent.qqmusic/databases/player_process_db' | gzip | base64" \
|
||||||
|
| base64 -d | gzip -d player_process_db
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 选择提取的这个文件即可。
|
@ -2,6 +2,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
Flex,
|
Flex,
|
||||||
|
Heading,
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
ModalCloseButton,
|
ModalCloseButton,
|
||||||
@ -14,16 +15,14 @@ import {
|
|||||||
TabPanel,
|
TabPanel,
|
||||||
TabPanels,
|
TabPanels,
|
||||||
Tabs,
|
Tabs,
|
||||||
useToast,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FileInput } from '~/components/FileInput';
|
import { FileInput } from '~/components/FileInput';
|
||||||
|
import mdHelpAndroid from './DocAndroid.md?raw';
|
||||||
|
import { MarkdownContent } from '~/components/MarkdownContent';
|
||||||
import { qmc2ImportKeys } from '../../settingsSlice';
|
import { qmc2ImportKeys } from '../../settingsSlice';
|
||||||
import { useAppDispatch } from '~/hooks';
|
import { useAppDispatch } from '~/hooks';
|
||||||
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
|
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
|
||||||
|
|
||||||
import { QMCv2AndroidInstructions } from './QMCv2AndroidInstructions';
|
|
||||||
|
|
||||||
export interface ImportFileModalProps {
|
export interface ImportFileModalProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -31,7 +30,6 @@ export interface ImportFileModalProps {
|
|||||||
|
|
||||||
export function ImportFileModal({ onClose, show }: ImportFileModalProps) {
|
export function ImportFileModal({ onClose, show }: ImportFileModalProps) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const toast = useToast();
|
|
||||||
const handleFileReceived = async (files: File[]) => {
|
const handleFileReceived = async (files: File[]) => {
|
||||||
try {
|
try {
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
@ -43,13 +41,6 @@ export function ImportFileModal({ onClose, show }: ImportFileModalProps) {
|
|||||||
if (qmc2Keys) {
|
if (qmc2Keys) {
|
||||||
dispatch(qmc2ImportKeys(qmc2Keys));
|
dispatch(qmc2ImportKeys(qmc2Keys));
|
||||||
onClose();
|
onClose();
|
||||||
toast({
|
|
||||||
title: `导入成功 (${qmc2Keys.length})`,
|
|
||||||
description: '记得保存更改来应用。',
|
|
||||||
isClosable: true,
|
|
||||||
duration: 5000,
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
alert(`不是支持的 SQLite 数据库文件。\n表名:${qmc2Keys}`);
|
alert(`不是支持的 SQLite 数据库文件。\n表名:${qmc2Keys}`);
|
||||||
@ -66,27 +57,31 @@ export function ImportFileModal({ onClose, show }: ImportFileModalProps) {
|
|||||||
<Modal isOpen={show} onClose={onClose} scrollBehavior="inside" size="xl">
|
<Modal isOpen={show} onClose={onClose} scrollBehavior="inside" size="xl">
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>导入密钥数据库</ModalHeader>
|
<ModalHeader>导入数据库</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<Flex as={ModalBody} gap={2} flexDir="column" flex={1}>
|
<Flex as={ModalBody} gap={2} flexDir="column">
|
||||||
<Center>
|
<Center>
|
||||||
<FileInput onReceiveFiles={handleFileReceived}>拖放或点我选择含有密钥的数据库文件</FileInput>
|
<FileInput onReceiveFiles={handleFileReceived}>拖放或点我选择含有密钥的数据库文件</FileInput>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
<Flex as={Tabs} variant="enclosed" flexDir="column" mt={4} flex={1} minH={0}>
|
<Heading as="h2" size="md" mt="4">
|
||||||
|
使用说明
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
<Tabs variant="enclosed">
|
||||||
<TabList>
|
<TabList>
|
||||||
<Tab>安卓客户端</Tab>
|
<Tab>安卓</Tab>
|
||||||
{/* <Tab>Two</Tab> */}
|
{/* <Tab>Two</Tab> */}
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels flex={1} overflow="auto">
|
<TabPanels>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<QMCv2AndroidInstructions />
|
<MarkdownContent>{mdHelpAndroid}</MarkdownContent>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<p>two!</p>
|
<p>two!</p>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Flex>
|
</Tabs>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionButton,
|
|
||||||
AccordionIcon,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionPanel,
|
|
||||||
Box,
|
|
||||||
Heading,
|
|
||||||
Link,
|
|
||||||
ListItem,
|
|
||||||
OrderedList,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
|
||||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
||||||
import hljsStyleGitHub from 'react-syntax-highlighter/dist/esm/styles/hljs/github';
|
|
||||||
|
|
||||||
import PowerShellAdbDumpCommand from './adb_dump.ps1?raw';
|
|
||||||
import ShellAdbDumpCommand from './adb_dump.sh?raw';
|
|
||||||
|
|
||||||
export function QMCv2AndroidInstructions() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Text>
|
|
||||||
你可能需要 <code>root</code> 或类似的访问权限。
|
|
||||||
</Text>
|
|
||||||
<Text>绝大多数情况下,这会导致你的安卓设备失去保修资格。</Text>
|
|
||||||
|
|
||||||
<Accordion allowToggle mt="2">
|
|
||||||
<AccordionItem>
|
|
||||||
<Heading as="h3" size="md">
|
|
||||||
<AccordionButton>
|
|
||||||
<Box as="span" flex="1" textAlign="left">
|
|
||||||
在安卓手机端操作
|
|
||||||
</Box>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Heading>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<OrderedList>
|
|
||||||
<ListItem>
|
|
||||||
使用具有 <code>root</code> 权限的文件浏览器,访问 <code>/data/data/com.tencent.qqmusic/databases/</code>
|
|
||||||
{' 目录,将文件 '}
|
|
||||||
<code>player_process_db</code> 复制到正常模式下用户可访问的目录(如下载目录)。
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>提交该数据库文件。</ListItem>
|
|
||||||
</OrderedList>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem>
|
|
||||||
<Heading as="h3" size="md">
|
|
||||||
<AccordionButton>
|
|
||||||
<Box as="span" flex="1" textAlign="left">
|
|
||||||
在 PC 端操作(ADB / PowerShell)
|
|
||||||
</Box>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Heading>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<OrderedList>
|
|
||||||
<ListItem>
|
|
||||||
确保 <code>adb</code> 命令可用。
|
|
||||||
<br />
|
|
||||||
💡 如果没有,可以
|
|
||||||
<Link href="https://scoop.sh/#/apps?q=adb" isExternal color="blue.600">
|
|
||||||
使用 Scoop 安装 <ExternalLinkIcon />
|
|
||||||
</Link>
|
|
||||||
。
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>启动终端并进入 PowerShell 7 环境。</ListItem>
|
|
||||||
<ListItem>将安卓设备连接到电脑,并允许调试。</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
粘贴执行下述代码。若设备提示「超级用户请求」请允许:
|
|
||||||
<SyntaxHighlighter language="ps1" style={hljsStyleGitHub}>
|
|
||||||
{PowerShellAdbDumpCommand}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
提交当前目录下的 <code>player_process_db</code> 文件。
|
|
||||||
</ListItem>
|
|
||||||
</OrderedList>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
|
|
||||||
<AccordionItem>
|
|
||||||
<Heading as="h3" size="md">
|
|
||||||
<AccordionButton>
|
|
||||||
<Box as="span" flex="1" textAlign="left">
|
|
||||||
在 Linux / Mac 系统下操作(ADB / Shell)
|
|
||||||
</Box>
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
</Heading>
|
|
||||||
<AccordionPanel pb={4}>
|
|
||||||
<OrderedList>
|
|
||||||
<ListItem>将安卓设备连接到电脑,并允许调试。</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
粘贴执行下述代码。若设备提示「超级用户请求」请允许:
|
|
||||||
<SyntaxHighlighter language="bash" style={hljsStyleGitHub}>
|
|
||||||
{ShellAdbDumpCommand}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem>
|
|
||||||
提交当前目录下的 <code>player_process_db</code> 文件。
|
|
||||||
</ListItem>
|
|
||||||
</OrderedList>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
try {
|
|
||||||
$gz_b64 = adb shell su -c "cat '/data/data/com.tencent.qqmusic/databases/player_process_db' | gzip | base64" | Out-String
|
|
||||||
$bStream = New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String($gz_b64))
|
|
||||||
$decoded = New-Object System.IO.Compression.GzipStream($bStream, [System.IO.Compression.CompressionMode]::Decompress)
|
|
||||||
$outFile = New-Object System.IO.FileStream("player_process_db", [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
|
|
||||||
$decoded.CopyTo($outFile)
|
|
||||||
} finally {
|
|
||||||
if ($outFile -ne $null) { $outFile.Dispose() }
|
|
||||||
if ($decoded -ne $null) { $decoded.Dispose() }
|
|
||||||
if ($bStream -ne $null) { $bStream.Dispose() }
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
sh adb shell su -c "cat '/data/data/com.tencent.qqmusic/databases/player_process_db' | gzip | base64" \
|
|
||||||
| base64 -d | gzip -d player_process_db
|
|
@ -3,13 +3,6 @@ import ReactDOM from 'react-dom/client';
|
|||||||
|
|
||||||
import { AppRoot } from './components/AppRoot';
|
import { AppRoot } from './components/AppRoot';
|
||||||
|
|
||||||
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
||||||
import hljsSyntaxPowerShell from 'react-syntax-highlighter/dist/esm/languages/hljs/powershell';
|
|
||||||
import hljsSyntaxBash from 'react-syntax-highlighter/dist/esm/languages/hljs/bash';
|
|
||||||
|
|
||||||
SyntaxHighlighter.registerLanguage('ps1', hljsSyntaxPowerShell);
|
|
||||||
SyntaxHighlighter.registerLanguage('bash', hljsSyntaxBash);
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppRoot />
|
<AppRoot />
|
||||||
|
@ -96,10 +96,8 @@ export default defineConfig({
|
|||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
manualChunks: {
|
manualChunks: {
|
||||||
reacts: ['react', 'react-dom', 'react-dropzone', 'react-promise-suspense', 'react-redux', '@reduxjs/toolkit'],
|
reacts: ['react', 'react-dom', 'react-promise-suspense', 'react-redux', '@reduxjs/toolkit'],
|
||||||
chakra: ['@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion'],
|
chakra: ['@chakra-ui/icons', '@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion'],
|
||||||
icons: ['react-icons', '@chakra-ui/icons'],
|
|
||||||
utility: ['radash', 'nanoid', 'immer', 'react-syntax-highlighter'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -12,10 +12,6 @@ export default defineConfig({
|
|||||||
replacement: 'src/$1',
|
replacement: 'src/$1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// workaround: sql.js is not ESModule friendly, yet...
|
|
||||||
deps: {
|
|
||||||
inline: ['sql.js'],
|
|
||||||
},
|
|
||||||
api: {
|
api: {
|
||||||
port: 5174, // vite port + 1
|
port: 5174, // vite port + 1
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user