feat: experimental support for douban key import
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
鲁树人 2023-10-19 02:58:50 +01:00
parent 57c0a247c0
commit 59c4678d1a
7 changed files with 74 additions and 21 deletions

View File

@ -36,10 +36,19 @@ export function AndroidADBPullInstruction({ dir, file }: AndroidADBPullInstructi
return (
<>
<Text>
<code>root</code> 访访
<ruby>
<rp> (</rp>
<rt>
<code>root</code>
</rt>
<rp>)</rp>
</ruby>
访访
</Text>
<Text>
<code>root</code>
<chakra.span color="red.400"></chakra.span>
</Text>
@ -92,7 +101,7 @@ export function AndroidADBPullInstruction({ dir, file }: AndroidADBPullInstructi
<OrderedList>
<ListItem>
<Text>
<code>adb</code>
<Code>adb</Code>
</Text>
<Text>
💡
@ -134,6 +143,11 @@ export function AndroidADBPullInstruction({ dir, file }: AndroidADBPullInstructi
</Heading>
<AccordionPanel pb={4}>
<OrderedList>
<ListItem>
<Text>
<Code>adb</Code>
</Text>
</ListItem>
<ListItem>
<Text></Text>
</ListItem>

View File

@ -35,7 +35,9 @@ export function ImportSecretModal({ clientName, children, show, onClose, onImpor
<FileInput onReceiveFiles={handleFileReceived}></FileInput>
</Center>
<Text mt={2}>{clientName && <>{clientName}</>}</Text>
<Text as="div" mt={2}>
{clientName && <>{clientName}</>}
</Text>
<Flex as={Tabs} variant="enclosed" flexDir="column" flex={1} minH={0}>
{children}
</Flex>

View File

@ -1,7 +1,7 @@
import { Alert, AlertIcon, Container, Flex, 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 { QMCv2QQMusicAllInstructions } from '~/features/settings/panels/QMCv2/QMCv2QQMusicAllInstructions';
import { SegmentKeyImportInstructions } from './SegmentKeyImportInstructions';
export function QQMusicFAQ() {
@ -35,7 +35,7 @@ export function QQMusicFAQ() {
</Alert>
</Container>
<SegmentKeyImportInstructions tab="QMCv2 密钥" clientInstructions={<QMCv2AllInstructions />} />
<SegmentKeyImportInstructions tab="QMCv2 密钥" clientInstructions={<QMCv2QQMusicAllInstructions />} />
</ListItem>
</List>
</>

View File

@ -14,6 +14,7 @@ import {
MenuDivider,
MenuItem,
MenuList,
Select,
Text,
Tooltip,
useToast,
@ -30,13 +31,15 @@ import { StagingQMCv2Key } from '../keyFormats';
import { DatabaseKeyExtractor } from '~/util/DatabaseKeyExtractor';
import { MMKVParser } from '~/util/MMKVParser';
import { getFileName } from '~/util/pathHelper';
import { QMCv2AllInstructions } from './QMCv2/QMCv2AllInstructions';
import { QMCv2QQMusicAllInstructions } from './QMCv2/QMCv2QQMusicAllInstructions';
import { QMCv2DoubanAllInstructions } from './QMCv2/QMCv2DoubanAllInstructions';
export function PanelQMCv2Key() {
const toast = useToast();
const dispatch = useDispatch();
const { keys: qmc2Keys, allowFuzzyNameSearch } = useSelector(selectStagingQMCv2Settings);
const [showImportModal, setShowImportModal] = useState(false);
const [secretType, setSecretType] = useState<'qm' | 'douban'>('qm');
const addKey = () => dispatch(qmc2AddKey());
const clearAll = () => dispatch(qmc2ClearKeys());
@ -51,11 +54,11 @@ export function PanelQMCv2Key() {
let qmc2Keys: null | Omit<StagingQMCv2Key, 'id'>[] = null;
if (/[_.]db$/i.test(file.name)) {
if (/(player_process[_.]db|music_audio_play)(\.db)?$/i.test(file.name)) {
const extractor = await DatabaseKeyExtractor.getInstance();
qmc2Keys = extractor.extractQmAndroidDbKeys(fileBuffer);
qmc2Keys = extractor.extractQmcV2KeysFromSqliteDb(fileBuffer);
if (!qmc2Keys) {
alert(`不是支持的 SQLite 数据库文件。\n表名${qmc2Keys}`);
alert(`不是支持的 SQLite 数据库文件。`);
return;
}
} else if (/MMKVStreamEncryptId|filenameEkeyMap/i.test(file.name)) {
@ -96,8 +99,8 @@ export function PanelQMCv2Key() {
</Heading>
<Text>
QQ QMCv2使QQ Mac iOS
线
QQ FM QMCv2使QQ Mac iOS 使
FM线
</Text>
<HStack pb={2} pt={2}>
@ -155,16 +158,28 @@ export function PanelQMCv2Key() {
<QMCv2EKeyItem key={id} id={id} ekey={ekey} name={name} i={i} />
))}
</List>
{qmc2Keys.length === 0 && <Text></Text>}
{qmc2Keys.length === 0 && <Text></Text>}
</Box>
<ImportSecretModal
clientName="QQ 音乐"
clientName={
<Select
value={secretType}
onChange={(e) => setSecretType(e.target.value as 'qm' | 'douban')}
variant="flushed"
display="inline"
css={{ paddingLeft: '0.75rem', width: 'auto' }}
>
<option value="qm">QQ </option>
<option value="douban"> FM</option>
</Select>
}
show={showImportModal}
onClose={() => setShowImportModal(false)}
onImport={handleSecretImport}
>
<QMCv2AllInstructions />
{secretType === 'qm' && <QMCv2QQMusicAllInstructions />}
{secretType === 'douban' && <QMCv2DoubanAllInstructions />}
</ImportSecretModal>
</Flex>
);

View File

@ -0,0 +1,17 @@
import { Tab, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
import { AndroidADBPullInstruction } from '~/components/AndroidADBPullInstruction/AndroidADBPullInstruction';
export function QMCv2DoubanAllInstructions() {
return (
<>
<TabList>
<Tab></Tab>
</TabList>
<TabPanels flex={1} overflow="auto">
<TabPanel>
<AndroidADBPullInstruction dir="/data/data/com.douban.radio/databases" file="music_audio_play" />
</TabPanel>
</TabPanels>
</>
);
}

View File

@ -4,7 +4,7 @@ import { InstructionsIOS } from './InstructionsIOS';
import { InstructionsMac } from './InstructionsMac';
import { InstructionsPC } from './InstructionsPC';
export function QMCv2AllInstructions() {
export function QMCv2QQMusicAllInstructions() {
return (
<>
<TabList>

View File

@ -23,16 +23,21 @@ export class DatabaseKeyExtractor {
return tables.includes(name);
}
extractQmAndroidDbKeys(buffer: ArrayBuffer): null | QMAndroidKeyEntry[] {
extractQmcV2KeysFromSqliteDb(buffer: ArrayBuffer): null | QMAndroidKeyEntry[] {
let db: SQLDatabase | null = null;
try {
db = new this.SQL.Database(new Uint8Array(buffer));
if (!this.hasTable(db, 'audio_file_ekey_table')) {
return null;
}
const result = db.exec('select file_path, ekey from audio_file_ekey_table');
let sql: undefined | string;
if (this.hasTable(db, 'audio_file_ekey_table')) {
sql = 'select file_path, ekey from audio_file_ekey_table';
} else if (this.hasTable(db, 'EKeyFileInfo')) {
sql = 'select filePath, eKey from EKeyFileInfo';
}
if (!sql) return null;
const result = db.exec(sql);
if (result.length === 0) {
return [];
}