Compare commits
2 Commits
c3809b48f7
...
af61d23fd4
Author | SHA1 | Date | |
---|---|---|---|
af61d23fd4 | |||
c6c373f9fc |
4
.env
Normal file
4
.env
Normal file
@ -0,0 +1,4 @@
|
||||
# Example environment file for vite to use.
|
||||
# For more information, see: https://vitejs.dev/guide/env-and-mode.html
|
||||
|
||||
ENABLE_PERF_LOG=0
|
@ -4,6 +4,11 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>音乐解锁 - Unlock Music</title>
|
||||
|
||||
<meta name="description" content="音乐解锁 - Unlock Music" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="/pwa-512x512.png" sizes="512x512" />
|
||||
<meta name="theme-color" content="#4DBA87" />
|
||||
</head>
|
||||
<body>
|
||||
<main id="root"></main>
|
||||
|
@ -54,6 +54,7 @@
|
||||
"prettier": "^2.8.8",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.2",
|
||||
"vite-plugin-pwa": "^0.15.0",
|
||||
"vite-plugin-top-level-await": "^1.3.0",
|
||||
"vite-plugin-wasm": "^3.2.2",
|
||||
"vitest": "^0.31.0"
|
||||
|
2012
pnpm-lock.yaml
2012
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
BIN
public/favicon-16x16.png
Normal file
BIN
public/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 641 B |
BIN
public/pwa-192x192.png
Normal file
BIN
public/pwa-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
public/pwa-512x512.png
Normal file
BIN
public/pwa-512x512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
1
src/decrypt-worker/util/DecryptError.ts
Normal file
1
src/decrypt-worker/util/DecryptError.ts
Normal file
@ -0,0 +1 @@
|
||||
export class UnsupportedSourceFile extends Error {}
|
@ -1,5 +1,6 @@
|
||||
import { Transformer, Parakeet, TransformResult, fetchParakeet } from '@jixun/libparakeet';
|
||||
import { toArrayBuffer } from './buffer';
|
||||
import { UnsupportedSourceFile } from './DecryptError';
|
||||
|
||||
export async function transformBlob(
|
||||
blob: Blob | ArrayBuffer,
|
||||
@ -21,7 +22,9 @@ export async function transformBlob(
|
||||
cleanup.push(() => writer.delete());
|
||||
|
||||
const result = transformer.Transform(writer, reader);
|
||||
if (result !== TransformResult.OK) {
|
||||
if (result === TransformResult.ERROR_INVALID_FORMAT) {
|
||||
throw new UnsupportedSourceFile(`transformer<${transformer.Name}> does not recognize this file`);
|
||||
} else if (result !== TransformResult.OK) {
|
||||
throw new Error(`transformer<${transformer.Name}> failed with error: ${TransformResult[result]} (${result})`);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Parakeet, fetchParakeet } from '@jixun/libparakeet';
|
||||
import { timedLogger } from '~/util/timedLogger';
|
||||
import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils';
|
||||
import { allCryptoFactories } from '../../crypto/CryptoFactory';
|
||||
import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer';
|
||||
import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase';
|
||||
import { UnsupportedSourceFile } from '~/decrypt-worker/util/DecryptError';
|
||||
|
||||
// Use first 4MiB of the file to perform check.
|
||||
const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024;
|
||||
@ -29,8 +30,11 @@ class DecryptCommandHandler {
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('decrypt failed: ', error);
|
||||
continue;
|
||||
if (error instanceof UnsupportedSourceFile) {
|
||||
console.debug('WARN: decryptor does not recognize source file, wrong crypto?', error);
|
||||
} else {
|
||||
console.error('decrypt failed with unknown error: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,18 +87,12 @@ class DecryptCommandHandler {
|
||||
|
||||
export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => {
|
||||
const label = `decrypt( ${id} )`;
|
||||
console.group(label);
|
||||
return withTimeGroupedLogs(label, async () => {
|
||||
const parakeet = await timedLogger(`${label}/init`, fetchParakeet);
|
||||
const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob()));
|
||||
const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer());
|
||||
|
||||
try {
|
||||
return await timedLogger(`${label}/total`, async () => {
|
||||
const parakeet = await timedLogger(`${label}/init`, fetchParakeet);
|
||||
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 handler = new DecryptCommandHandler(id, parakeet, blob, buffer);
|
||||
return handler.decrypt(allCryptoFactories);
|
||||
});
|
||||
} finally {
|
||||
(console.groupEnd as (label: string) => void)(label);
|
||||
}
|
||||
const handler = new DecryptCommandHandler(id, parakeet, blob, buffer);
|
||||
return handler.decrypt(allCryptoFactories);
|
||||
});
|
||||
};
|
||||
|
@ -2,21 +2,19 @@ function isPromise<T = unknown>(p: unknown): p is Promise<T> {
|
||||
return !!p && typeof p === 'object' && 'then' in p && 'catch' in p && 'finally' in p;
|
||||
}
|
||||
|
||||
export function timedLogger<R = unknown>(label: string, fn: () => R): R {
|
||||
console.time(label);
|
||||
export function wrapFunctionCall<R = unknown>(pre: () => void, post: () => void, fn: () => R): R {
|
||||
pre();
|
||||
|
||||
try {
|
||||
const result = fn();
|
||||
|
||||
if (isPromise(result)) {
|
||||
result.finally(() => {
|
||||
console.timeEnd(label);
|
||||
});
|
||||
result.finally(post);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.timeEnd(label);
|
||||
post();
|
||||
throw e;
|
||||
}
|
||||
}
|
25
src/util/logUtils.ts
Normal file
25
src/util/logUtils.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { wrapFunctionCall } from './fnWrapper';
|
||||
|
||||
export function timedLogger<R = unknown>(label: string, fn: () => R): R {
|
||||
if (import.meta.env.ENABLE_PERF_LOG !== '1') {
|
||||
return fn();
|
||||
} else {
|
||||
return wrapFunctionCall(
|
||||
() => console.time(label),
|
||||
() => console.timeEnd(label),
|
||||
fn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function withGroupedLogs<R = unknown>(label: string, fn: () => R): R {
|
||||
if (import.meta.env.ENABLE_PERF_LOG !== '1') {
|
||||
return fn();
|
||||
} else {
|
||||
return wrapFunctionCall(
|
||||
() => console.group(label),
|
||||
() => (console.groupEnd as (label: string) => void)(label),
|
||||
() => timedLogger(`${label}/total`, fn)
|
||||
);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import react from '@vitejs/plugin-react';
|
||||
import wasm from 'vite-plugin-wasm';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
const gitRoot = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
const pkg = JSON.parse(fs.readFileSync(gitRoot + '/package.json', 'utf-8'));
|
||||
@ -52,6 +53,34 @@ export default defineConfig({
|
||||
react(),
|
||||
wasm(),
|
||||
topLevelAwait(),
|
||||
VitePWA({
|
||||
registerType: 'prompt',
|
||||
workbox: {
|
||||
// Cache everything from dist
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,wasm}'],
|
||||
},
|
||||
manifest: {
|
||||
display: 'standalone',
|
||||
name: '音乐解锁 (Unlock Music)',
|
||||
short_name: '音乐解锁',
|
||||
lang: 'zh-cmn-Hans-CN',
|
||||
description: '在现代浏览器解锁已购的加密音乐!',
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
Loading…
Reference in New Issue
Block a user