1
0
mirror of synced 2026-05-22 14:43:19 +00:00

Generate Web ReadableStream by converting Node.js Readable

This commit is contained in:
Borewit
2025-01-20 20:30:45 +01:00
parent a29bcc2172
commit b4be9c7809
3 changed files with 2518 additions and 3877 deletions
+2 -2
View File
@@ -2,7 +2,7 @@ import fs from 'node:fs';
import * as mm from '../lib/index.js'; import * as mm from '../lib/index.js';
import type { IAudioMetadata, IOptions } from '../lib/index.js'; import type { IAudioMetadata, IOptions } from '../lib/index.js';
import { makeReadableByteFileStream } from './util.js'; import { makeByteReadableStreamFromFile } from './util.js';
type ParseFileMethod = (skipTest: () => void, filePath: string, mimeType?: string, options?: IOptions) => Promise<IAudioMetadata>; type ParseFileMethod = (skipTest: () => void, filePath: string, mimeType?: string, options?: IOptions) => Promise<IAudioMetadata>;
@@ -39,7 +39,7 @@ export const Parsers: IParser[] = [
description: 'parseWebStream', description: 'parseWebStream',
webStream: true, webStream: true,
initParser: async (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { initParser: async (skipTest, filePath: string, mimeType?: string, options?: IOptions) => {
const webStream = await makeReadableByteFileStream(filePath); const webStream = await makeByteReadableStreamFromFile(filePath);
try { try {
return await mm.parseWebStream(webStream.stream, {mimeType: mimeType, size: webStream.fileSize}, options); return await mm.parseWebStream(webStream.stream, {mimeType: mimeType, size: webStream.fileSize}, options);
} finally { } finally {
+61 -41
View File
@@ -3,8 +3,9 @@
import { Readable } from 'node:stream'; import { Readable } from 'node:stream';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { createReadStream } from 'node:fs';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import { ReadableStream } from 'node:stream/web'; import type {ReadableByteStreamController, ReadableStreamBYOBRequest} from 'node:stream/web';
const filename = fileURLToPath(import.meta.url); const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename); const dirname = path.dirname(filename);
@@ -24,52 +25,71 @@ export class SourceStream extends Readable {
} }
} }
export async function makeReadableByteFileStream(filename: string, delay = 0): Promise<{ fileSize: number, stream: ReadableStream<Uint8Array>, closeFile: () => Promise<void> }> { export async function makeByteReadableStreamFromFile(filename: string): Promise<{ fileSize: number, stream: ReadableStream<Uint8Array>, closeFile: () => Promise<void> }> {
let position = 0;
const fileInfo = await fs.stat(filename); const fileInfo = await fs.stat(filename);
const fileHandle = await fs.open(filename, 'r'); const nodeStream = createReadStream(filename);
return { return {
fileSize: fileInfo.size, fileSize: fileInfo.size,
stream: new ReadableStream({ stream: makeByteReadableStreamFromNodeReadable(nodeStream),
type: 'bytes', closeFile: () => Promise.resolve()
async pull(controller) {
// @ts-ignore
const view = controller.byobRequest.view;
setTimeout(async () => {
try {
const {bytesRead} = await fileHandle.read(view, 0, view.byteLength, position);
if (bytesRead === 0) {
await fileHandle.close();
controller.close();
// @ts-ignore
controller.byobRequest.respond(0);
} else {
position += bytesRead;
// @ts-ignore
controller.byobRequest.respond(bytesRead);
}
} catch (err) {
controller.error(err);
await fileHandle.close();
}
}, delay);
},
cancel() {
return fileHandle.close();
},
autoAllocateChunkSize: 1024
}),
closeFile: () => {
return fileHandle.close();
}
}; };
} }
function makeByteReadableStreamFromNodeReadable(nodeReadable: Readable): ReadableStream<Uint8Array> {
return new ReadableStream<Uint8Array>({
type: 'bytes',
start(controller: ReadableByteStreamController) {
const onData = (chunk: Buffer) => {
if (controller.byobRequest) {
const view = (controller.byobRequest as ReadableStreamBYOBRequest).view;
const bytesToCopy = Math.min(view.byteLength, chunk.byteLength);
new Uint8Array(view.buffer, view.byteOffset, view.byteLength)
.set(new Uint8Array(chunk.buffer, chunk.byteOffset, bytesToCopy));
(controller.byobRequest as ReadableStreamBYOBRequest).respond(bytesToCopy);
if (bytesToCopy < chunk.byteLength) {
controller.enqueue(chunk.subarray(bytesToCopy));
}
} else {
controller.enqueue(new Uint8Array(chunk));
}
};
const onEnd = () => {
controller.close();
cleanup();
};
const onError = (err: Error) => {
controller.error(err);
cleanup();
};
const cleanup = () => {
nodeReadable.off('data', onData);
nodeReadable.off('end', onEnd);
nodeReadable.off('error', onError);
};
nodeReadable.on('data', onData);
nodeReadable.on('end', onEnd);
nodeReadable.on('error', onError);
nodeReadable.resume();
},
pull(controller) {
if (nodeReadable.isPaused()) {
nodeReadable.resume();
}
},
cancel(reason) {
nodeReadable.destroy(reason);
}
});
}
export const samplePath = path.join(dirname, 'samples'); export const samplePath = path.join(dirname, 'samples');
+2455 -3834
View File
File diff suppressed because it is too large Load Diff