mirror of
https://git.unlock-music.dev/um/um-react.git
synced 2024-11-23 23:22:18 +00:00
Compare commits
No commits in common. "e9a95d1bd63142fd693ae9fafa1126926e7ac8cd" and "5e890bca7799b3c0a558eb86a63fe2b7822b8e8b" have entirely different histories.
e9a95d1bd6
...
5e890bca77
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "um-react",
|
"name": "um-react",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.3.2",
|
"version": "0.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
|
@ -38,32 +38,20 @@ export class QQMusicV2Decipher implements DecipherInstance {
|
|||||||
this.cipherName = `QQMusic/QMC2(user_key=${+useUserKey})`;
|
this.cipherName = `QQMusic/QMC2(user_key=${+useUserKey})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseFooter(buffer: Uint8Array): { size: number; ekey?: undefined | string } {
|
async decrypt(buffer: Uint8Array, options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
||||||
const footer = QMCFooter.parse(buffer.subarray(buffer.byteLength - 1024));
|
const footer = QMCFooter.parse(buffer.subarray(buffer.byteLength - 1024));
|
||||||
|
if (!footer) {
|
||||||
if (footer) {
|
|
||||||
const { size, ekey } = footer;
|
|
||||||
footer.free();
|
|
||||||
return { size, ekey };
|
|
||||||
}
|
|
||||||
|
|
||||||
// No footer, and we don't accept user key:
|
|
||||||
if (!this.useUserKey) {
|
|
||||||
throw new UnsupportedSourceFile('Not QMC2 File');
|
throw new UnsupportedSourceFile('Not QMC2 File');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { size: 0 };
|
const audioBuffer = buffer.slice(0, buffer.byteLength - footer.size);
|
||||||
}
|
|
||||||
|
|
||||||
async decrypt(buffer: Uint8Array, options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
|
||||||
const footer = this.parseFooter(buffer.subarray(buffer.byteLength - 1024));
|
|
||||||
const ekey = this.useUserKey ? options.qmc2Key : footer.ekey;
|
const ekey = this.useUserKey ? options.qmc2Key : footer.ekey;
|
||||||
|
footer.free();
|
||||||
if (!ekey) {
|
if (!ekey) {
|
||||||
throw new Error('EKey required');
|
throw new Error('EKey missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
const qmc2 = new QMC2(ekey);
|
const qmc2 = new QMC2(ekey);
|
||||||
const audioBuffer = buffer.slice(0, buffer.byteLength - footer.size);
|
|
||||||
for (const [block, offset] of chunkBuffer(audioBuffer)) {
|
for (const [block, offset] of chunkBuffer(audioBuffer)) {
|
||||||
qmc2.decrypt(block, offset);
|
qmc2.decrypt(block, offset);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
chakra,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
chakra,
|
|
||||||
Flex,
|
Flex,
|
||||||
HStack,
|
HStack,
|
||||||
Icon,
|
Icon,
|
||||||
@ -19,9 +19,9 @@ import {
|
|||||||
TabPanels,
|
TabPanels,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
|
VStack,
|
||||||
useBreakpointValue,
|
useBreakpointValue,
|
||||||
useToast,
|
useToast,
|
||||||
VStack,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { PanelQMCv2Key } from './panels/PanelQMCv2Key';
|
import { PanelQMCv2Key } from './panels/PanelQMCv2Key';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@ -145,7 +145,7 @@ export function Settings() {
|
|||||||
onClick={handleResetSettings}
|
onClick={handleResetSettings}
|
||||||
colorScheme="red"
|
colorScheme="red"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
title="放弃未储存的更改,将设定还原未储存前的状态。"
|
title="放弃未储存的更改,将设定还原为储存前的状态。"
|
||||||
aria-label="放弃未储存的更改"
|
aria-label="放弃未储存的更改"
|
||||||
/>
|
/>
|
||||||
<Button onClick={handleApplySettings}>保存</Button>
|
<Button onClick={handleApplySettings}>保存</Button>
|
||||||
|
@ -1,36 +1,12 @@
|
|||||||
import { Alert, AlertIcon, Box, Button, Flex, Text, VStack } from '@chakra-ui/react';
|
import { Box, VStack } from '@chakra-ui/react';
|
||||||
import { SelectFile } from '../components/SelectFile';
|
import { SelectFile } from '../components/SelectFile';
|
||||||
|
|
||||||
import { FileListing } from '~/features/file-listing/FileListing';
|
import { FileListing } from '~/features/file-listing/FileListing';
|
||||||
import { useAppDispatch, useAppSelector } from '~/hooks.ts';
|
|
||||||
import { selectIsSettingsNotSaved } from '~/features/settings/settingsSelector.ts';
|
|
||||||
import { commitStagingChange } from '~/features/settings/settingsSlice.ts';
|
|
||||||
|
|
||||||
export function MainTab() {
|
export function MainTab() {
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const isSettingsNotSaved = useAppSelector(selectIsSettingsNotSaved);
|
|
||||||
const onClickSaveSettings = () => {
|
|
||||||
dispatch(commitStagingChange());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box h="full" w="full" pt="4">
|
<Box h="full" w="full" pt="4">
|
||||||
<VStack gap="3">
|
<VStack gap="3">
|
||||||
{isSettingsNotSaved && (
|
|
||||||
<Alert borderRadius={7} maxW={400} status="warning">
|
|
||||||
<AlertIcon />
|
|
||||||
<Flex flexDir="row" alignItems="center" flexGrow={1} justifyContent="space-between">
|
|
||||||
<Text m={0}>
|
|
||||||
有尚未储存的设置,
|
|
||||||
<br />
|
|
||||||
设定将在保存后生效
|
|
||||||
</Text>
|
|
||||||
<Button type="button" ml={3} size="md" onClick={onClickSaveSettings}>
|
|
||||||
立即储存
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
<SelectFile />
|
<SelectFile />
|
||||||
|
|
||||||
<Box w="full">
|
<Box w="full">
|
||||||
|
Loading…
Reference in New Issue
Block a user