feat: added support for QMCv2 (#11)
This commit is contained in:
parent
08c082ec2c
commit
8ab267019c
@ -14,7 +14,7 @@
|
|||||||
"@chakra-ui/react": "^2.6.1",
|
"@chakra-ui/react": "^2.6.1",
|
||||||
"@emotion/react": "^11.11.0",
|
"@emotion/react": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@jixun/libparakeet": "0.0.0-exp.15",
|
"@jixun/libparakeet": "0.0.0-exp.16",
|
||||||
"@reduxjs/toolkit": "^1.9.5",
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
"framer-motion": "^10.12.8",
|
"framer-motion": "^10.12.8",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
|
@ -14,8 +14,8 @@ dependencies:
|
|||||||
specifier: ^11.11.0
|
specifier: ^11.11.0
|
||||||
version: 11.11.0(@emotion/react@11.11.0)(@types/react@18.0.28)(react@18.2.0)
|
version: 11.11.0(@emotion/react@11.11.0)(@types/react@18.0.28)(react@18.2.0)
|
||||||
'@jixun/libparakeet':
|
'@jixun/libparakeet':
|
||||||
specifier: 0.0.0-exp.15
|
specifier: 0.0.0-exp.16
|
||||||
version: 0.0.0-exp.15
|
version: 0.0.0-exp.16
|
||||||
'@reduxjs/toolkit':
|
'@reduxjs/toolkit':
|
||||||
specifier: ^1.9.5
|
specifier: ^1.9.5
|
||||||
version: 1.9.5(react-redux@8.0.5)(react@18.2.0)
|
version: 1.9.5(react-redux@8.0.5)(react@18.2.0)
|
||||||
@ -1780,8 +1780,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@jixun/libparakeet@0.0.0-exp.15:
|
/@jixun/libparakeet@0.0.0-exp.16:
|
||||||
resolution: {integrity: sha512-Le2Gl/2V6BsrK8NcFTtaelUkjqewCmDT3xZgUxOKjOo2lK1qvj1AtxarhfDtsOQVjvItiibuRPaRFPpLmWJe9g==}
|
resolution: {integrity: sha512-jDj9kju0tCJyesV+yi4xcOGClrFgdarkcG/aGP3tRfonf1l8y11A8lVwbp3cWXJcrgHAn7683rTEUYitg9TfpA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@jridgewell/gen-mapping@0.3.3:
|
/@jridgewell/gen-mapping@0.3.3:
|
||||||
|
3
src/decrypt-worker/crypto/qmc/qmc_v2.key.ts
Normal file
3
src/decrypt-worker/crypto/qmc/qmc_v2.key.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const SEED = 106;
|
||||||
|
export const ENC_V2_KEY_1 = '386ZJY!@#*$%^&)(';
|
||||||
|
export const ENC_V2_KEY_2 = '**#!(#$%&^a1cZ,T';
|
17
src/decrypt-worker/crypto/qmc/qmc_v2.ts
Normal file
17
src/decrypt-worker/crypto/qmc/qmc_v2.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { transformBlob } from '~/decrypt-worker/util/transformBlob';
|
||||||
|
import type { CryptoBase } from '../CryptoBase';
|
||||||
|
import { SEED, ENC_V2_KEY_1, ENC_V2_KEY_2 } from './qmc_v2.key.ts';
|
||||||
|
|
||||||
|
export class QMC2Crypto implements CryptoBase {
|
||||||
|
hasSignature(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isSupported(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async decrypt(blob: Blob): Promise<Blob> {
|
||||||
|
return transformBlob(blob, (p) => p.make.QMCv2(p.make.QMCv2FooterParser(SEED, ENC_V2_KEY_1, ENC_V2_KEY_2)));
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,11 @@ import { WorkerServerBus } from '~/util/WorkerEventBus';
|
|||||||
import { DECRYPTION_WORKER_ACTION_NAME } from './constants';
|
import { DECRYPTION_WORKER_ACTION_NAME } from './constants';
|
||||||
|
|
||||||
import type { CryptoFactory } from './crypto/CryptoBase';
|
import type { CryptoFactory } from './crypto/CryptoBase';
|
||||||
|
import { fetchParakeet } from '@jixun/libparakeet';
|
||||||
|
|
||||||
import { XiamiCrypto } from './crypto/xiami/xiami';
|
import { XiamiCrypto } from './crypto/xiami/xiami';
|
||||||
import { QMC1Crypto } from './crypto/qmc/qmc_v1';
|
import { QMC1Crypto } from './crypto/qmc/qmc_v1';
|
||||||
import { fetchParakeet } from '@jixun/libparakeet';
|
import { QMC2Crypto } from './crypto/qmc/qmc_v2';
|
||||||
|
|
||||||
const bus = new WorkerServerBus();
|
const bus = new WorkerServerBus();
|
||||||
onmessage = bus.onmessage;
|
onmessage = bus.onmessage;
|
||||||
@ -15,6 +17,9 @@ const decryptorFactories: CryptoFactory[] = [
|
|||||||
|
|
||||||
// QMCv1 (*.qmcflac)
|
// QMCv1 (*.qmcflac)
|
||||||
() => new QMC1Crypto(),
|
() => new QMC1Crypto(),
|
||||||
|
|
||||||
|
// QMCv2 (*.mflac)
|
||||||
|
() => new QMC2Crypto(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Use first 4MiB of the file to perform check.
|
// Use first 4MiB of the file to perform check.
|
||||||
@ -27,17 +32,22 @@ bus.addEventHandler(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, async (blobURI) => {
|
|||||||
for (const factory of decryptorFactories) {
|
for (const factory of decryptorFactories) {
|
||||||
const decryptor = factory();
|
const decryptor = factory();
|
||||||
if (await decryptor.isSupported(blob)) {
|
if (await decryptor.isSupported(blob)) {
|
||||||
const decryptedBlob = await decryptor.decrypt(blob);
|
try {
|
||||||
|
const decryptedBlob = await decryptor.decrypt(blob);
|
||||||
|
|
||||||
// Check if we had a successful decryption
|
// Check if we had a successful decryption
|
||||||
const header = await decryptedBlob.slice(0, TEST_FILE_HEADER_LEN).arrayBuffer();
|
const header = await decryptedBlob.slice(0, TEST_FILE_HEADER_LEN).arrayBuffer();
|
||||||
const audioExt = parakeet.detectAudioExtension(header);
|
const audioExt = parakeet.detectAudioExtension(header);
|
||||||
if (!decryptor.hasSignature() && audioExt === 'bin') {
|
if (!decryptor.hasSignature() && audioExt === 'bin') {
|
||||||
// skip this decryptor result
|
// skip this decryptor result
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { decrypted: URL.createObjectURL(decryptedBlob), ext: audioExt };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('decrypt failed: ', error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { decrypted: URL.createObjectURL(decryptedBlob), ext: audioExt };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user