Compare commits

..

No commits in common. "af61d23fd4d1a3b80afa2184f189b71604eae7aa" and "c3809b48f7fa1f2d029844b5fb2e25587b547c19" have entirely different histories.

13 changed files with 35 additions and 2087 deletions

4
.env
View File

@ -1,4 +0,0 @@
# Example environment file for vite to use.
# For more information, see: https://vitejs.dev/guide/env-and-mode.html
ENABLE_PERF_LOG=0

View File

@ -4,11 +4,6 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>音乐解锁 - Unlock Music</title> <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> </head>
<body> <body>
<main id="root"></main> <main id="root"></main>

View File

@ -54,7 +54,6 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.3.2", "vite": "^4.3.2",
"vite-plugin-pwa": "^0.15.0",
"vite-plugin-top-level-await": "^1.3.0", "vite-plugin-top-level-await": "^1.3.0",
"vite-plugin-wasm": "^3.2.2", "vite-plugin-wasm": "^3.2.2",
"vitest": "^0.31.0" "vitest": "^0.31.0"

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1 +0,0 @@
export class UnsupportedSourceFile extends Error {}

View File

@ -1,6 +1,5 @@
import { Transformer, Parakeet, TransformResult, fetchParakeet } from '@jixun/libparakeet'; import { Transformer, Parakeet, TransformResult, fetchParakeet } from '@jixun/libparakeet';
import { toArrayBuffer } from './buffer'; import { toArrayBuffer } from './buffer';
import { UnsupportedSourceFile } from './DecryptError';
export async function transformBlob( export async function transformBlob(
blob: Blob | ArrayBuffer, blob: Blob | ArrayBuffer,
@ -22,9 +21,7 @@ export async function transformBlob(
cleanup.push(() => writer.delete()); cleanup.push(() => writer.delete());
const result = transformer.Transform(writer, reader); const result = transformer.Transform(writer, reader);
if (result === TransformResult.ERROR_INVALID_FORMAT) { if (result !== TransformResult.OK) {
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})`); throw new Error(`transformer<${transformer.Name}> failed with error: ${TransformResult[result]} (${result})`);
} }

View File

@ -1,9 +1,8 @@
import { Parakeet, fetchParakeet } from '@jixun/libparakeet'; import { Parakeet, fetchParakeet } from '@jixun/libparakeet';
import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils'; import { timedLogger } from '~/util/timedLogger';
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';
import { UnsupportedSourceFile } from '~/decrypt-worker/util/DecryptError';
// Use first 4MiB of the file to perform check. // Use first 4MiB of the file to perform check.
const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024; const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024;
@ -30,11 +29,8 @@ class DecryptCommandHandler {
} }
return result; return result;
} catch (error) { } catch (error) {
if (error instanceof UnsupportedSourceFile) { console.error('decrypt failed: ', error);
console.debug('WARN: decryptor does not recognize source file, wrong crypto?', error); continue;
} else {
console.error('decrypt failed with unknown error: ', error);
}
} }
} }
@ -87,7 +83,10 @@ class DecryptCommandHandler {
export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => { export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => {
const label = `decrypt( ${id} )`; const label = `decrypt( ${id} )`;
return withTimeGroupedLogs(label, async () => { console.group(label);
try {
return await timedLogger(`${label}/total`, 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());
@ -95,4 +94,7 @@ export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobUR
const handler = new DecryptCommandHandler(id, parakeet, blob, buffer); const handler = new DecryptCommandHandler(id, parakeet, blob, buffer);
return handler.decrypt(allCryptoFactories); return handler.decrypt(allCryptoFactories);
}); });
} finally {
(console.groupEnd as (label: string) => void)(label);
}
}; };

View File

@ -1,25 +0,0 @@
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)
);
}
}

View File

@ -2,19 +2,21 @@ function isPromise<T = unknown>(p: unknown): p is Promise<T> {
return !!p && typeof p === 'object' && 'then' in p && 'catch' in p && 'finally' in p; return !!p && typeof p === 'object' && 'then' in p && 'catch' in p && 'finally' in p;
} }
export function wrapFunctionCall<R = unknown>(pre: () => void, post: () => void, fn: () => R): R { export function timedLogger<R = unknown>(label: string, fn: () => R): R {
pre(); console.time(label);
try { try {
const result = fn(); const result = fn();
if (isPromise(result)) { if (isPromise(result)) {
result.finally(post); result.finally(() => {
console.timeEnd(label);
});
} }
return result; return result;
} catch (e) { } catch (e) {
post(); console.timeEnd(label);
throw e; throw e;
} }
} }

View File

@ -8,7 +8,6 @@ import react from '@vitejs/plugin-react';
import wasm from 'vite-plugin-wasm'; import wasm from 'vite-plugin-wasm';
import replace from '@rollup/plugin-replace'; import replace from '@rollup/plugin-replace';
import topLevelAwait from 'vite-plugin-top-level-await'; import topLevelAwait from 'vite-plugin-top-level-await';
import { VitePWA } from 'vite-plugin-pwa';
const gitRoot = url.fileURLToPath(new URL('.', import.meta.url)); const gitRoot = url.fileURLToPath(new URL('.', import.meta.url));
const pkg = JSON.parse(fs.readFileSync(gitRoot + '/package.json', 'utf-8')); const pkg = JSON.parse(fs.readFileSync(gitRoot + '/package.json', 'utf-8'));
@ -53,34 +52,6 @@ export default defineConfig({
react(), react(),
wasm(), wasm(),
topLevelAwait(), 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: { resolve: {
alias: { alias: {