Merge main into release for tag v1.23.0

This commit is contained in:
github-actions[bot]
2025-10-02 14:03:04 +00:00
8 changed files with 117 additions and 9 deletions
+3
View File
@@ -1,3 +1,6 @@
> [!NOTE]
> I'm on vacation until 18 October, so expect slow or no replies to issues during that time. 🏖️
# Mediabunny - JavaScript media toolkit
[![](https://img.shields.io/npm/v/mediabunny)](https://www.npmjs.com/package/mediabunny)
+28 -1
View File
@@ -290,7 +290,7 @@ By default, data will be emitted by the `StreamTarget` as soon as it is availabl
new StreamTarget(writable, {
chunked: true,
chunkSize: 2 ** 20, // Optional; defaults to 16 MiB
}),
});
```
#### Applying backpressure
@@ -329,6 +329,33 @@ const output = new Output({
await output.finalize(); // Will automatically close the writable stream
```
### `FilePathTarget`
This target writes to a file at the specified path. It is intended for server-side usage in Node, Bun, or Deno, and offers a simpler API than `StreamTarget` when you just want to write directly to a file path.
```ts
import { Output, FilePathTarget } from 'mediabunny';
const output = new Output({
target: new FilePathTarget('/path/to/output.mp4'),
// ...
});
// ...
await output.finalize(); // Will automatically close the file handle
```
The internally held file handle will be closed when `finalize` or `cancel` are called on the `Output`.
Writing is chunked by default, for performance. Like `StreamTarget`, you can configure chunked mode options:
```ts
new FilePathTarget('/path/to/output.mp4', {
chunked: false, // Disable chunking (slower)
chunkSize: 2 ** 20, // Optional; defaults to 16 MiB
});
```
### `NullTarget`
This target simply discards all data that is passed into it. It is useful for when you need an `Output` but extract data from it differently, for example through output format-specific callbacks or encoder events.
+6 -6
View File
@@ -1,12 +1,12 @@
{
"name": "mediabunny",
"version": "1.22.0",
"version": "1.23.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mediabunny",
"version": "1.22.0",
"version": "1.23.0",
"license": "MPL-2.0",
"workspaces": [
"packages/*"
@@ -7749,9 +7749,9 @@
}
},
"node_modules/mediabunny": {
"version": "1.21.1",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.21.1.tgz",
"integrity": "sha512-heBCNei4nBRJ2jkA08LL2zDZVyV9ADsSwFWOaRn37PEjUr6A46vlD+qlUYVTSSv5iIjIt1YTqTP7Nqo+q1hEvw==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.22.0.tgz",
"integrity": "sha512-T8zsXwrRtKEAlITRizn0RiJc1GllVMw3Zcze5ACyluTy3jndUPhAM4SrwjR2SWgqqUa+4nlJ3MmyAi2XuRxwFg==",
"license": "MPL-2.0",
"peer": true,
"workspaces": [
@@ -12242,7 +12242,7 @@
},
"packages/mp3-encoder": {
"name": "@mediabunny/mp3-encoder",
"version": "1.22.0",
"version": "1.23.0",
"license": "MPL-2.0",
"devDependencies": {
"@types/emscripten": "^1.40.1"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "mediabunny",
"author": "Vanilagy",
"version": "1.22.0",
"version": "1.23.0",
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
"type": "module",
"workspaces": [
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@mediabunny/mp3-encoder",
"author": "Vanilagy",
"version": "1.22.0",
"version": "1.23.0",
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
+2
View File
@@ -92,6 +92,8 @@ export {
export {
Target,
BufferTarget,
FilePathTarget,
FilePathTargetOptions,
NullTarget,
StreamTarget,
StreamTargetOptions,
+7
View File
@@ -1224,6 +1224,7 @@ class ReadOrchestrator {
workers: ReadWorker[] = [];
cache: CacheEntry[] = [];
currentCacheSize = 0;
disposed = false;
constructor(public options: {
maxCacheSize: number;
@@ -1472,6 +1473,11 @@ class ReadOrchestrator {
/** Called by a worker when it has read some data. */
supplyWorkerData(worker: ReadWorker, bytes: Uint8Array) {
if (this.disposed) {
// Writes may still come in after disposal, but we just ignore those
return;
}
const start = worker.currentPos;
const end = start + bytes.length;
@@ -1645,5 +1651,6 @@ class ReadOrchestrator {
this.workers.length = 0;
this.cache.length = 0;
this.disposed = true;
}
}
+69
View File
@@ -6,8 +6,15 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import type { FileHandle } from 'node:fs/promises';
import { BufferTargetWriter, NullTargetWriter, StreamTargetWriter, Writer } from './writer';
import { Output } from './output';
import * as nodeAlias from './node';
import { assert } from './misc';
const node = typeof nodeAlias !== 'undefined'
? nodeAlias // Aliasing it prevents some bundler warnings
: undefined!;
/**
* Base class for targets, specifying where output files are written.
@@ -121,6 +128,68 @@ export class StreamTarget extends Target {
}
}
/**
* Options for {@link FilePathTarget}.
* @group Output targets
* @public
*/
export type FilePathTargetOptions = StreamTargetOptions;
/**
* A target that writes to a file at the specified path. Intended for server-side usage in Node, Bun, or Deno.
*
* Writing is chunked by default. The internally held file handle will be closed when `.finalize()` or `.cancel()` are
* called on the corresponding {@link Output}.
* @group Output targets
* @public
*/
export class FilePathTarget extends Target {
/** @internal */
_streamTarget: StreamTarget;
/** @internal */
_fileHandle: FileHandle | null = null;
/** Creates a new {@link FilePathTarget} that writes to the file at the specified file path. */
constructor(filePath: string, options: FilePathTargetOptions = {}) {
if (typeof filePath !== 'string') {
throw new TypeError('filePath must be a string.');
}
if (!options || typeof options !== 'object') {
throw new TypeError('options must be an object.');
}
super();
// Let's back this target with a StreamTarget, makes the implementation very simple
const writable = new WritableStream<StreamTargetChunk>({
start: async () => {
this._fileHandle = await node.fs.open(filePath, 'w');
},
write: async (chunk) => {
assert(this._fileHandle);
await this._fileHandle.write(chunk.data, 0, chunk.data.byteLength, chunk.position);
},
close: async () => {
if (this._fileHandle) {
await this._fileHandle.close();
this._fileHandle = null;
}
},
});
this._streamTarget = new StreamTarget(writable, {
chunked: true,
...options,
});
this._streamTarget._output = this._output;
}
/** @internal */
_createWriter(): Writer {
return this._streamTarget._createWriter();
}
}
/**
* This target just discards all incoming data. It is useful for when you need an {@link Output} but extract data from
* it differently, for example through format-specific callbacks (`onMoof`, `onMdat`, ...) or encoder events.