@bendyline/squisq-video-react 2.5.14 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/index.js +1 -1
- package/dist/vite/index.d.ts +51 -0
- package/dist/vite/index.js +72 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -220,6 +220,27 @@ available, and otherwise skipped with `audioIncluded: false` plus an
|
|
|
220
220
|
completes. GIF skips audio preparation entirely. `supportsWebCodecsH264(config)` probes a specific encoder
|
|
221
221
|
configuration; `EncoderConfig` and `FfmpegWasmLoadConfig` are also exported.
|
|
222
222
|
|
|
223
|
+
### RNNoise WASM for Vite hosts (recommended)
|
|
224
|
+
|
|
225
|
+
Media-edit denoise uses RNNoise, whose package embeds a 3.6 MB WASM as base64.
|
|
226
|
+
A host that bundles the media-edit worker and its main-thread fallback gets that
|
|
227
|
+
payload twice. Add the plugin to both the page and the worker builds and it
|
|
228
|
+
ships once, as an emitted `rnnoise-*.wasm` fetched on first use:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// vite.config.ts
|
|
232
|
+
import { rnnoiseWasmPlugin } from '@bendyline/squisq-video-react/vite';
|
|
233
|
+
|
|
234
|
+
export default defineConfig({
|
|
235
|
+
plugins: [rnnoiseWasmPlugin()],
|
|
236
|
+
worker: { plugins: () => [rnnoiseWasmPlugin()] },
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
It works for ES and IIFE (Vite's default) worker formats and follows `base`
|
|
241
|
+
the way Vite's own asset URLs do. The fetch must be allowed by `connect-src`.
|
|
242
|
+
Without the plugin, everything still works with the self-contained modules.
|
|
243
|
+
|
|
223
244
|
## Audio export
|
|
224
245
|
|
|
225
246
|
`@bendyline/squisq-video-react/encoder` also exports a streaming audio-file
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
supportsWebCodecsAac,
|
|
31
31
|
supportsWebCodecsH264
|
|
32
32
|
} from "./chunk-RIXBIX3H.js";
|
|
33
|
+
import "./chunk-5MFQMJ5Z.js";
|
|
33
34
|
import {
|
|
34
35
|
createMediaEditRenderManager,
|
|
35
36
|
createMediaEditRenderer,
|
|
@@ -38,7 +39,6 @@ import {
|
|
|
38
39
|
useProcessedAudio
|
|
39
40
|
} from "./chunk-TRCTDIWS.js";
|
|
40
41
|
import "./chunk-TL2IMDIZ.js";
|
|
41
|
-
import "./chunk-5MFQMJ5Z.js";
|
|
42
42
|
export {
|
|
43
43
|
CoverImageExportModal,
|
|
44
44
|
DEFAULT_VIDEO_COVER_PRE_ROLL_SECONDS,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin that ships RNNoise's WASM as one file instead of twice in base64.
|
|
3
|
+
*
|
|
4
|
+
* The pinned `@shiguredo/rnnoise-wasm` embeds its 3.6 MB WASM as base64 in
|
|
5
|
+
* JavaScript. A host bundling the media-edit engine gets that payload twice:
|
|
6
|
+
* statically inside the worker, and again in the main-thread fallback's lazy
|
|
7
|
+
* chunk. This plugin extracts it to an emitted `rnnoise.wasm` that both load
|
|
8
|
+
* only when `Rnnoise.load()` runs, keeping the upstream runtime. Hosts add it
|
|
9
|
+
* to `plugins` and to `worker.plugins`; dev servers keep the self-contained
|
|
10
|
+
* module.
|
|
11
|
+
*
|
|
12
|
+
* The URL is written once the output format is known, by Vite's own rule: an
|
|
13
|
+
* absolute `base` names the file outright, a relative one resolves it against
|
|
14
|
+
* the running chunk. `import.meta.ROLLUP_FILE_URL` cannot be used: an IIFE
|
|
15
|
+
* worker (Vite's default) has no `import.meta`, so Vite 8 writes `{}.url` and
|
|
16
|
+
* the fetch throws.
|
|
17
|
+
*/
|
|
18
|
+
/** The plugin-context methods this plugin calls (Rollup and Rolldown both provide them). */
|
|
19
|
+
interface RnnoisePluginContext {
|
|
20
|
+
emitFile(file: {
|
|
21
|
+
type: 'asset';
|
|
22
|
+
name: string;
|
|
23
|
+
source: Uint8Array;
|
|
24
|
+
}): string;
|
|
25
|
+
getFileName(referenceId: string): string;
|
|
26
|
+
error(message: string): never;
|
|
27
|
+
}
|
|
28
|
+
/** A Vite plugin, declared structurally so this package needs no Vite dependency. */
|
|
29
|
+
interface RnnoiseWasmPlugin {
|
|
30
|
+
name: string;
|
|
31
|
+
apply: 'build';
|
|
32
|
+
enforce: 'pre';
|
|
33
|
+
configResolved(config: {
|
|
34
|
+
base: string;
|
|
35
|
+
}): void;
|
|
36
|
+
transform(this: RnnoisePluginContext, code: string, id: string): {
|
|
37
|
+
code: string;
|
|
38
|
+
map: null;
|
|
39
|
+
} | null;
|
|
40
|
+
renderChunk(this: RnnoisePluginContext, code: string, chunk: {
|
|
41
|
+
fileName: string;
|
|
42
|
+
}, options: {
|
|
43
|
+
format: string;
|
|
44
|
+
}): {
|
|
45
|
+
code: string;
|
|
46
|
+
map: null;
|
|
47
|
+
} | null;
|
|
48
|
+
}
|
|
49
|
+
declare function rnnoiseWasmPlugin(): RnnoiseWasmPlugin;
|
|
50
|
+
|
|
51
|
+
export { type RnnoiseWasmPlugin, rnnoiseWasmPlugin };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/vite/rnnoiseWasmPlugin.ts
|
|
2
|
+
var MODULE_SUFFIX = "/@shiguredo/rnnoise-wasm/dist/rnnoise.js";
|
|
3
|
+
var WASM_MAGIC = [0, 97, 115, 109, 1, 0, 0, 0];
|
|
4
|
+
var URL_PLACEHOLDER = "__SQUISQ_RNNOISE_WASM_URL__";
|
|
5
|
+
var SCRIPT_URL = "(typeof document === 'undefined' ? self.location.href : (document.currentScript && document.currentScript.src) || document.baseURI)";
|
|
6
|
+
function rnnoiseWasmPlugin() {
|
|
7
|
+
let base;
|
|
8
|
+
let wasmReference;
|
|
9
|
+
return {
|
|
10
|
+
name: "squisq-rnnoise-wasm-asset",
|
|
11
|
+
apply: "build",
|
|
12
|
+
enforce: "pre",
|
|
13
|
+
configResolved(config) {
|
|
14
|
+
base = config.base;
|
|
15
|
+
},
|
|
16
|
+
transform(code, id) {
|
|
17
|
+
if (!id.replaceAll("\\", "/").endsWith(MODULE_SUFFIX)) return null;
|
|
18
|
+
const factory = /function (\w+)\(\) \{\s*return \w+\("([A-Za-z0-9+/=]{1000,})"\);\s*\}/.exec(
|
|
19
|
+
code
|
|
20
|
+
);
|
|
21
|
+
if (!factory) this.error("RNNoise embedded WASM factory changed; update the asset plugin.");
|
|
22
|
+
const [, name, base64] = factory;
|
|
23
|
+
const load = /static async load\(\) \{/g;
|
|
24
|
+
if ([...code.matchAll(load)].length !== 1) {
|
|
25
|
+
this.error("RNNoise load method changed; update the asset plugin.");
|
|
26
|
+
}
|
|
27
|
+
const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
|
|
28
|
+
if (!WASM_MAGIC.every((byte, index) => bytes[index] === byte)) {
|
|
29
|
+
this.error("RNNoise embedded payload is not a WebAssembly binary.");
|
|
30
|
+
}
|
|
31
|
+
wasmReference = this.emitFile({ type: "asset", name: "rnnoise.wasm", source: bytes });
|
|
32
|
+
return {
|
|
33
|
+
// Fetch before entering Emscripten: a failed request must not leave
|
|
34
|
+
// its initialization promise or dependency-monitor timer running.
|
|
35
|
+
code: `let squisqRnnoiseBytes;
|
|
36
|
+
async function squisqFetchRnnoiseWasm() {
|
|
37
|
+
const response = await fetch(${URL_PLACEHOLDER});
|
|
38
|
+
if (!response.ok) throw new Error('RNNoise WASM load failed: ' + response.status);
|
|
39
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
40
|
+
}
|
|
41
|
+
` + code.replace(factory[0], `function ${name}() { return squisqRnnoiseBytes; }`).replace(
|
|
42
|
+
load,
|
|
43
|
+
"static async load() { squisqRnnoiseBytes ??= await squisqFetchRnnoiseWasm();"
|
|
44
|
+
),
|
|
45
|
+
map: null
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
renderChunk(code, chunk, options) {
|
|
49
|
+
if (!wasmReference || !code.includes(URL_PLACEHOLDER)) return null;
|
|
50
|
+
const file = this.getFileName(wasmReference);
|
|
51
|
+
return {
|
|
52
|
+
code: code.replaceAll(URL_PLACEHOLDER, wasmUrl(file, chunk.fileName, options.format, base)),
|
|
53
|
+
map: null
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function wasmUrl(file, chunk, format, base) {
|
|
59
|
+
if (base && /^(?:[a-z][a-z\d+.-]*:)?\//i.test(base)) return JSON.stringify(base + file);
|
|
60
|
+
const script = format === "es" ? "import.meta.url" : SCRIPT_URL;
|
|
61
|
+
return `new URL(${JSON.stringify(relativePath(chunk, file))}, ${script}).href`;
|
|
62
|
+
}
|
|
63
|
+
function relativePath(from, to) {
|
|
64
|
+
const fromDirs = from.split("/").slice(0, -1);
|
|
65
|
+
const toParts = to.split("/");
|
|
66
|
+
let shared = 0;
|
|
67
|
+
while (shared < fromDirs.length && fromDirs[shared] === toParts[shared]) shared++;
|
|
68
|
+
return [...fromDirs.slice(shared).map(() => ".."), ...toParts.slice(shared)].join("/");
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
rnnoiseWasmPlugin
|
|
72
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-video-react",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.16",
|
|
4
4
|
"description": "React components for browser-based MP4 and animated-GIF export of Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -57,6 +57,10 @@
|
|
|
57
57
|
"./media-edit": {
|
|
58
58
|
"types": "./dist/media-edit/index.d.ts",
|
|
59
59
|
"import": "./dist/media-edit/index.js"
|
|
60
|
+
},
|
|
61
|
+
"./vite": {
|
|
62
|
+
"types": "./dist/vite/index.d.ts",
|
|
63
|
+
"import": "./dist/vite/index.js"
|
|
60
64
|
}
|
|
61
65
|
},
|
|
62
66
|
"files": [
|
|
@@ -77,9 +81,9 @@
|
|
|
77
81
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
78
82
|
},
|
|
79
83
|
"dependencies": {
|
|
80
|
-
"@bendyline/squisq": "2.13.
|
|
81
|
-
"@bendyline/squisq-video": "2.4.
|
|
82
|
-
"@bendyline/squisq-react": "2.12.
|
|
84
|
+
"@bendyline/squisq": "2.13.5",
|
|
85
|
+
"@bendyline/squisq-video": "2.4.11",
|
|
86
|
+
"@bendyline/squisq-react": "2.12.15",
|
|
83
87
|
"@ffmpeg/core": "0.12.10",
|
|
84
88
|
"@ffmpeg/ffmpeg": "0.12.15",
|
|
85
89
|
"@ffmpeg/util": "0.12.2",
|