Dependency upgrade + lib_um_crypto_rust (#78)

Co-authored-by: 鲁树人 <lu.shuren@um-react.app>
Co-committed-by: 鲁树人 <lu.shuren@um-react.app>
This commit is contained in:
鲁树人
2024-09-24 22:19:30 +00:00
committed by 鲁树人
parent c5bc436ab2
commit 58c96f264b
65 changed files with 4160 additions and 4025 deletions

View File

@@ -1,4 +1,4 @@
import { chakra, Box, Button, Collapse, Text, useDisclosure } from '@chakra-ui/react';
import { Box, Button, chakra, Collapse, Text, useDisclosure } from '@chakra-ui/react';
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
export interface FileErrorProps {
@@ -18,11 +18,12 @@ export function FileError({ error, code }: FileErrorProps) {
<Box>
<Text>
<chakra.span>
<chakra.span color="red.700">{errorSummary}</chakra.span>
<chakra.span color="red.700">{errorSummary}</chakra.span>
</chakra.span>
{error && (
<Button ml="2" onClick={onToggle} type="button">
</Button>
)}
</Text>

View File

@@ -1,12 +1,17 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import type { RootState } from '~/store';
import { DECRYPTION_WORKER_ACTION_NAME, type DecryptionResult } from '~/decrypt-worker/constants';
import type { DecryptCommandOptions, FetchMusicExNamePayload } from '~/decrypt-worker/types';
import type {
DecryptCommandOptions,
FetchMusicExNamePayload,
ParseKuwoHeaderPayload,
ParseKuwoHeaderResponse,
} from '~/decrypt-worker/types';
import { decryptionQueue, workerClientBus } from '~/decrypt-worker/client';
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
import { selectQMCv2KeyByFileName, selectKWMv2Key, selectQtfmAndroidKey } from '../settings/settingsSelector';
import { selectKWMv2Key, selectQMCv2KeyByFileName, selectQtfmAndroidKey } from '../settings/settingsSelector';
export enum ProcessState {
QUEUED = 'QUEUED',
@@ -43,6 +48,7 @@ export interface FileListingState {
files: Record<string, DecryptedAudioFile>;
displayMode: ListingMode;
}
const initialState: FileListingState = {
files: {},
displayMode: ListingMode.LIST,
@@ -64,28 +70,20 @@ export const processFile = createAsyncThunk<
thunkAPI.dispatch(setFileAsProcessing({ id: fileId }));
};
const fileHeader = await fetch(file.raw, { headers: { Range: 'bytes=0-1023' } })
.then((r) => r.blob())
.then((r) => r.arrayBuffer())
.then((r) => {
if (r.byteLength > 1024) {
return r.slice(0, 1024);
}
return r;
});
const qmcv2MusicExMediaFile = await workerClientBus.request<string, FetchMusicExNamePayload>(
DECRYPTION_WORKER_ACTION_NAME.FIND_QMC_MUSICEX_NAME,
{
id: fileId,
const [qmcv2MusicExMediaFile, kuwoHdr] = await Promise.all([
workerClientBus.request<string, FetchMusicExNamePayload>(DECRYPTION_WORKER_ACTION_NAME.FIND_QMC_MUSICEX_NAME, {
blobURI: file.raw,
},
);
}),
workerClientBus.request<ParseKuwoHeaderResponse, ParseKuwoHeaderPayload>(
DECRYPTION_WORKER_ACTION_NAME.KUWO_PARSE_HEADER,
{ blobURI: file.raw },
),
]);
const options: DecryptCommandOptions = {
fileName: file.fileName,
qmc2Key: selectQMCv2KeyByFileName(state, qmcv2MusicExMediaFile || file.fileName),
kwm2key: selectKWMv2Key(state, new DataView(fileHeader)),
kwm2key: selectKWMv2Key(state, kuwoHdr),
qingTingAndroidKey: selectQtfmAndroidKey(state),
};
return decryptionQueue.add({ id: fileId, blobURI: file.raw, options }, onPreProcess);

View File

@@ -4,8 +4,8 @@ import {
ButtonGroup,
Checkbox,
Flex,
HStack,
Heading,
HStack,
Icon,
IconButton,
List,
@@ -61,7 +61,7 @@ export function PanelQMCv2Key() {
alert(`不是支持的 SQLite 数据库文件。`);
return;
}
} else if (/MMKVStreamEncryptId|filenameEkeyMap|qmpc-mmkv-v1/i.test(file.name)) {
} else if (/MMKVStreamEncryptId|filenameEkeyMap|qmpc-mmkv-v1|(\.mmkv$)/i.test(file.name)) {
const fileBuffer = await file.arrayBuffer();
const map = parseAndroidQmEKey(new DataView(fileBuffer));
qmc2Keys = Array.from(map.entries(), ([name, ekey]) => ({ name: getFileName(name), ekey }));

View File

@@ -13,12 +13,14 @@ import {
} from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from '~/hooks';
import { fetchParakeet } from '@um/libparakeet';
import { ExtLink } from '~/components/ExtLink';
import { ChangeEvent, ClipboardEvent } from 'react';
import { VQuote } from '~/components/HelpText/VQuote';
import { selectStagingQtfmAndroidKey } from '../settingsSelector';
import { qtfmAndroidUpdateKey } from '../settingsSlice';
import { workerClientBus } from '~/decrypt-worker/client.ts';
import { GetQingTingFMDeviceKeyPayload } from '~/decrypt-worker/types.ts';
import { DECRYPTION_WORKER_ACTION_NAME } from '~/decrypt-worker/constants.ts';
const QTFM_DEVICE_ID_URL = 'https://github.com/parakeet-rs/qtfm-device-id/releases/latest';
@@ -38,31 +40,23 @@ export function PanelQingTing() {
return;
}
const dataMap = new Map();
for (const [_unused, key, value] of plainText.matchAll(
/^(PRODUCT|DEVICE|MANUFACTURER|BRAND|BOARD|MODEL): (.+)/gim,
)) {
dataMap.set(key.toLowerCase(), value);
const dataMap = Object.create(null);
for (const [, key, value] of plainText.matchAll(/^(PRODUCT|DEVICE|MANUFACTURER|BRAND|BOARD|MODEL): (.+)/gim)) {
dataMap[key.toLowerCase()] = value;
}
const { product, device, manufacturer, brand, board, model } = dataMap;
const product = dataMap.get('product') ?? null;
const device = dataMap.get('device') ?? null;
const manufacturer = dataMap.get('manufacturer') ?? null;
const brand = dataMap.get('brand') ?? null;
const board = dataMap.get('board') ?? null;
const model = dataMap.get('model') ?? null;
if (
product !== null &&
device !== null &&
manufacturer !== null &&
brand !== null &&
board !== null &&
model !== null
) {
if (product && device && manufacturer && brand && board && model) {
e.preventDefault();
fetchParakeet().then((parakeet) => {
setSecretKey(parakeet.qtfm.createDeviceKey(product, device, manufacturer, brand, board, model));
});
workerClientBus
.request<string, GetQingTingFMDeviceKeyPayload>(
DECRYPTION_WORKER_ACTION_NAME.QINGTING_FM_GET_DEVICE_KEY,
dataMap,
)
.then(setSecretKey)
.catch((err) => {
alert(`生成设备密钥时发生错误: ${err}`);
});
}
};

View File

@@ -1,8 +1,8 @@
import { parseKuwoHeader } from '~/crypto/parseKuwo';
import type { RootState } from '~/store';
import { closestByLevenshtein } from '~/util/levenshtein';
import { hasOwn } from '~/util/objects';
import { kwm2StagingToProductionKey } from './keyFormats';
import type { ParseKuwoHeaderResponse } from '~/decrypt-worker/types.ts';
export const selectIsSettingsNotSaved = (state: RootState) => state.settings.dirty;
@@ -31,14 +31,16 @@ export const selectQMCv2KeyByFileName = (state: RootState, name: string): string
return ekey;
};
export const selectKWMv2Key = (state: RootState, headerView: DataView): string | undefined => {
const hdr = parseKuwoHeader(headerView);
export const selectKWMv2Key = (state: RootState, hdr: ParseKuwoHeaderResponse): string | undefined => {
if (!hdr) {
return;
}
const quality = String(hdr.qualityId);
const rid = String(hdr.resourceId);
const keys = selectFinalKWMv2Keys(state);
const lookupKey = kwm2StagingToProductionKey({ id: '', ekey: '', quality: hdr.quality, rid: hdr.rid });
const lookupKey = kwm2StagingToProductionKey({ id: '', ekey: '', quality, rid });
let ekey: string | undefined;
if (hasOwn(keys, lookupKey)) {