refactor: move decrypt command to its own file; added version command

This commit is contained in:
鲁树人 2023-05-15 00:09:26 +01:00
parent b99590908e
commit cf3324e833
4 changed files with 58 additions and 47 deletions

View File

@ -9,7 +9,7 @@ export const workerClient = new Worker(new URL('./worker', import.meta.url), { t
workerClient.onerror = (err) => console.error(err); workerClient.onerror = (err) => console.error(err);
class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }> { class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }> {
constructor(private workerClientBus: WorkerClientBus, maxQueue?: number) { constructor(private workerClientBus: WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>, maxQueue?: number) {
super(maxQueue); super(maxQueue);
} }
@ -18,4 +18,5 @@ class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }> {
} }
} }
export const decryptionQueue = new DecryptionQueue(new WorkerClientBus(workerClient)); export const workerClientBus = new WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>(workerClient);
export const decryptionQueue = new DecryptionQueue(workerClientBus);

View File

@ -1,5 +1,6 @@
export enum DECRYPTION_WORKER_ACTION_NAME { export enum DECRYPTION_WORKER_ACTION_NAME {
DECRYPT = 'DECRYPT', DECRYPT = 'DECRYPT',
VERSION = 'VERSION',
} }
export interface DecryptionResult { export interface DecryptionResult {

View File

@ -0,0 +1,49 @@
import { fetchParakeet } from '@jixun/libparakeet';
import { CryptoFactory } from '../crypto/CryptoBase';
import { XiamiCrypto } from '../crypto/xiami/xiami';
import { QMC1Crypto } from '../crypto/qmc/qmc_v1';
import { QMC2Crypto } from '../crypto/qmc/qmc_v2';
// Use first 4MiB of the file to perform check.
const TEST_FILE_HEADER_LEN = 1024 * 1024 * 4;
const decryptorFactories: CryptoFactory[] = [
// Xiami (*.xm)
() => new XiamiCrypto(),
// QMCv1 (*.qmcflac)
() => new QMC1Crypto(),
// QMCv2 (*.mflac)
() => new QMC2Crypto(),
];
export const workerDecryptHandler = async (blobURI: string) => {
const blob = await fetch(blobURI).then((r) => r.blob());
const parakeet = await fetchParakeet();
for (const factory of decryptorFactories) {
const decryptor = factory();
if (await decryptor.isSupported(blob)) {
try {
const decryptedBlob = await decryptor.decrypt(blob);
// Check if we had a successful decryption
const header = await decryptedBlob.slice(0, TEST_FILE_HEADER_LEN).arrayBuffer();
const audioExt = parakeet.detectAudioExtension(header);
if (!decryptor.hasSignature() && audioExt === 'bin') {
// skip this decryptor result
continue;
}
return { decrypted: URL.createObjectURL(decryptedBlob), ext: audioExt };
} catch (error) {
console.error('decrypt failed: ', error);
continue;
}
}
}
throw new Error('could not decrypt file: no working decryptor found');
};

View File

@ -1,55 +1,15 @@
import { WorkerServerBus } from '~/util/WorkerEventBus'; 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 { getSDKVersion } from '@jixun/libparakeet';
import { fetchParakeet } from '@jixun/libparakeet';
import { XiamiCrypto } from './crypto/xiami/xiami'; import { workerDecryptHandler } from './worker-handler/decrypt';
import { QMC1Crypto } from './crypto/qmc/qmc_v1';
import { QMC2Crypto } from './crypto/qmc/qmc_v2';
const bus = new WorkerServerBus(); const bus = new WorkerServerBus();
onmessage = bus.onmessage; onmessage = bus.onmessage;
const decryptorFactories: CryptoFactory[] = [ bus.addEventHandler(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, workerDecryptHandler);
// Xiami (*.xm)
() => new XiamiCrypto(),
// QMCv1 (*.qmcflac) bus.addEventHandler(DECRYPTION_WORKER_ACTION_NAME.VERSION, async () => {
() => new QMC1Crypto(), return getSDKVersion();
// QMCv2 (*.mflac)
() => new QMC2Crypto(),
];
// Use first 4MiB of the file to perform check.
const TEST_FILE_HEADER_LEN = 1024 * 1024 * 4;
bus.addEventHandler(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, async (blobURI) => {
const blob = await fetch(blobURI).then((r) => r.blob());
const parakeet = await fetchParakeet();
for (const factory of decryptorFactories) {
const decryptor = factory();
if (await decryptor.isSupported(blob)) {
try {
const decryptedBlob = await decryptor.decrypt(blob);
// Check if we had a successful decryption
const header = await decryptedBlob.slice(0, TEST_FILE_HEADER_LEN).arrayBuffer();
const audioExt = parakeet.detectAudioExtension(header);
if (!decryptor.hasSignature() && audioExt === 'bin') {
// skip this decryptor result
continue;
}
return { decrypted: URL.createObjectURL(decryptedBlob), ext: audioExt };
} catch (error) {
console.error('decrypt failed: ', error);
continue;
}
}
}
throw new Error('could not decrypt file: no working decryptor found');
}); });