feat: pass options to downstream decryptor
This commit is contained in:
parent
f169a036d0
commit
4de0d5304d
@ -1,3 +1,5 @@
|
|||||||
|
import type { DecryptCommandOptions } from '~/decrypt-worker/types';
|
||||||
|
|
||||||
export interface CryptoBase {
|
export interface CryptoBase {
|
||||||
cryptoName: string;
|
cryptoName: string;
|
||||||
checkByDecryptHeader: boolean;
|
checkByDecryptHeader: boolean;
|
||||||
@ -8,8 +10,8 @@ export interface CryptoBase {
|
|||||||
*/
|
*/
|
||||||
overrideExtension?: string;
|
overrideExtension?: string;
|
||||||
|
|
||||||
checkBySignature?: (buffer: ArrayBuffer) => Promise<boolean>;
|
checkBySignature?: (buffer: ArrayBuffer, options: DecryptCommandOptions) => Promise<boolean>;
|
||||||
decrypt(buffer: ArrayBuffer, blob: Blob): Promise<Blob | ArrayBuffer>;
|
decrypt(buffer: ArrayBuffer, options: DecryptCommandOptions): Promise<Blob | ArrayBuffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CryptoFactory = () => CryptoBase;
|
export type CryptoFactory = () => CryptoBase;
|
||||||
|
9
src/decrypt-worker/types.ts
Normal file
9
src/decrypt-worker/types.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export interface DecryptCommandOptions {
|
||||||
|
qmc2Key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DecryptCommandPayload {
|
||||||
|
id: string;
|
||||||
|
blobURI: string;
|
||||||
|
options: DecryptCommandOptions;
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { Parakeet, fetchParakeet } from '@jixun/libparakeet';
|
import { Parakeet, fetchParakeet } from '@jixun/libparakeet';
|
||||||
import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils';
|
import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils';
|
||||||
|
import type { DecryptCommandOptions, DecryptCommandPayload } from '~/decrypt-worker/types';
|
||||||
import { allCryptoFactories } from '../../crypto/CryptoFactory';
|
import { allCryptoFactories } from '../../crypto/CryptoFactory';
|
||||||
import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer';
|
import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer';
|
||||||
import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase';
|
import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase';
|
||||||
@ -11,8 +12,13 @@ const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024;
|
|||||||
class DecryptCommandHandler {
|
class DecryptCommandHandler {
|
||||||
private label: string;
|
private label: string;
|
||||||
|
|
||||||
constructor(label: string, private parakeet: Parakeet, private blob: Blob, private buffer: ArrayBuffer) {
|
constructor(
|
||||||
this.label = `DecryptCommandHandler( ${label} )`;
|
label: string,
|
||||||
|
private parakeet: Parakeet,
|
||||||
|
private buffer: ArrayBuffer,
|
||||||
|
private options: DecryptCommandOptions
|
||||||
|
) {
|
||||||
|
this.label = `DecryptCommandHandler(${label})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
log<R>(label: string, fn: () => R): R {
|
log<R>(label: string, fn: () => R): R {
|
||||||
@ -42,7 +48,7 @@ class DecryptCommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async decryptFile(crypto: CryptoBase) {
|
async decryptFile(crypto: CryptoBase) {
|
||||||
if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer))) {
|
if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer, this.options))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +56,7 @@ class DecryptCommandHandler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.blob));
|
const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.options));
|
||||||
|
|
||||||
// Check if we had a successful decryption
|
// Check if we had a successful decryption
|
||||||
const audioExt = crypto.overrideExtension ?? (await this.detectAudioExtension(decrypted));
|
const audioExt = crypto.overrideExtension ?? (await this.detectAudioExtension(decrypted));
|
||||||
@ -76,23 +82,21 @@ class DecryptCommandHandler {
|
|||||||
|
|
||||||
// Check by decrypt max first 8MiB
|
// Check by decrypt max first 8MiB
|
||||||
const decryptedBuffer = await this.log(`${crypto.cryptoName}/decrypt-header-test`, async () =>
|
const decryptedBuffer = await this.log(`${crypto.cryptoName}/decrypt-header-test`, async () =>
|
||||||
toArrayBuffer(
|
toArrayBuffer(await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.options))
|
||||||
await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.blob.slice(0, TEST_FILE_HEADER_LEN))
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.parakeet.detectAudioExtension(decryptedBuffer) !== 'bin';
|
return this.parakeet.detectAudioExtension(decryptedBuffer) !== 'bin';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => {
|
export const workerDecryptHandler = async ({ id, blobURI, options }: DecryptCommandPayload) => {
|
||||||
const label = `decrypt( ${id} )`;
|
const label = `decrypt(${id})`;
|
||||||
return withTimeGroupedLogs(label, async () => {
|
return withTimeGroupedLogs(label, async () => {
|
||||||
const parakeet = await timedLogger(`${label}/init`, fetchParakeet);
|
const parakeet = await timedLogger(`${label}/init`, fetchParakeet);
|
||||||
const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob()));
|
const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob()));
|
||||||
const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer());
|
const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer());
|
||||||
|
|
||||||
const handler = new DecryptCommandHandler(id, parakeet, blob, buffer);
|
const handler = new DecryptCommandHandler(id, parakeet, buffer, options);
|
||||||
return handler.decrypt(allCryptoFactories);
|
return handler.decrypt(allCryptoFactories);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { decryptionQueue } from '~/decrypt-worker/client';
|
|||||||
|
|
||||||
import type { DecryptionResult } from '~/decrypt-worker/constants';
|
import type { DecryptionResult } from '~/decrypt-worker/constants';
|
||||||
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
|
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
|
||||||
|
import { selectDecryptOptionByFile } from '../settings/settingsSelector';
|
||||||
|
|
||||||
export enum ProcessState {
|
export enum ProcessState {
|
||||||
QUEUED = 'QUEUED',
|
QUEUED = 'QUEUED',
|
||||||
@ -51,7 +52,8 @@ export const processFile = createAsyncThunk<
|
|||||||
{ fileId: string },
|
{ fileId: string },
|
||||||
{ rejectValue: { message: string; stack?: string } }
|
{ rejectValue: { message: string; stack?: string } }
|
||||||
>('fileListing/processFile', async ({ fileId }, thunkAPI) => {
|
>('fileListing/processFile', async ({ fileId }, thunkAPI) => {
|
||||||
const file = selectFiles(thunkAPI.getState() as RootState)[fileId];
|
const state = thunkAPI.getState() as RootState;
|
||||||
|
const file = selectFiles(state)[fileId];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
const { message, stack } = new Error('ERROR: File not found');
|
const { message, stack } = new Error('ERROR: File not found');
|
||||||
return thunkAPI.rejectWithValue({ message, stack });
|
return thunkAPI.rejectWithValue({ message, stack });
|
||||||
@ -61,7 +63,8 @@ export const processFile = createAsyncThunk<
|
|||||||
thunkAPI.dispatch(setFileAsProcessing({ id: fileId }));
|
thunkAPI.dispatch(setFileAsProcessing({ id: fileId }));
|
||||||
};
|
};
|
||||||
|
|
||||||
return decryptionQueue.add({ id: fileId, blobURI: file.raw }, onPreProcess);
|
const options = selectDecryptOptionByFile(state, file.fileName);
|
||||||
|
return decryptionQueue.add({ id: fileId, blobURI: file.raw, options }, onPreProcess);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fileListingSlice = createSlice({
|
export const fileListingSlice = createSlice({
|
||||||
|
11
src/features/settings/settingsSelector.ts
Normal file
11
src/features/settings/settingsSelector.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { DecryptCommandOptions } from '~/decrypt-worker/types';
|
||||||
|
import type { RootState } from '~/store';
|
||||||
|
import { hasOwn } from '~/util/objects';
|
||||||
|
|
||||||
|
export const selectDecryptOptionByFile = (state: RootState, name: string): DecryptCommandOptions => {
|
||||||
|
const qmc2Keys = state.settings.qmc2.keys;
|
||||||
|
|
||||||
|
return {
|
||||||
|
qmc2Key: hasOwn(qmc2Keys, name) ? qmc2Keys[name] : undefined,
|
||||||
|
};
|
||||||
|
};
|
@ -1,13 +1,15 @@
|
|||||||
|
import type { DecryptCommandPayload } from '~/decrypt-worker/types';
|
||||||
import { DECRYPTION_WORKER_ACTION_NAME, DecryptionResult } from '~/decrypt-worker/constants';
|
import { DECRYPTION_WORKER_ACTION_NAME, DecryptionResult } from '~/decrypt-worker/constants';
|
||||||
|
|
||||||
import { ConcurrentQueue } from './ConcurrentQueue';
|
import { ConcurrentQueue } from './ConcurrentQueue';
|
||||||
import { WorkerClientBus } from './WorkerEventBus';
|
import { WorkerClientBus } from './WorkerEventBus';
|
||||||
|
|
||||||
export class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }, DecryptionResult> {
|
export class DecryptionQueue extends ConcurrentQueue<DecryptCommandPayload, DecryptionResult> {
|
||||||
constructor(private workerClientBus: WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>, maxQueue?: number) {
|
constructor(private workerClientBus: WorkerClientBus<DECRYPTION_WORKER_ACTION_NAME>, maxQueue?: number) {
|
||||||
super(maxQueue);
|
super(maxQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handler(item: { id: string; blobURI: string }): Promise<DecryptionResult> {
|
async handler(item: DecryptCommandPayload): Promise<DecryptionResult> {
|
||||||
return this.workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, item);
|
return this.workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,5 @@ export function* enumObject<T>(obj: Record<string, T> | null | void): Generator<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const { hasOwn } = Object;
|
||||||
|
Loading…
Reference in New Issue
Block a user