forked from forks/ffprobe-wasm-npm
Add project files
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "ffprobe-wasm-app"]
|
||||||
|
path = ffprobe-wasm-app
|
||||||
|
url = https://github.com/alfg/ffprobe-wasm
|
||||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022 Tomás Fox
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
42
README.md
Normal file
42
README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
ffprobe-wasm
|
||||||
|
==========
|
||||||
|
|
||||||
|
Gather information from multimedia streams. Works on the browser and Node.js.
|
||||||
|
|
||||||
|
Uses the code at [alfg/ffprobe-wasm](https://github.com/alfg/ffprobe-wasm), but in a packaged format, so it can be reused in other projects.
|
||||||
|
|
||||||
|
_Note_: This project doesn't build or use FFProbe. Instead it uses FFmpeg's libavformat and libavcodec to output similar results.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install ffprobe-wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Node.js
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { FFprobeWorker } from 'ffprobe-wasm/node.mjs';
|
||||||
|
|
||||||
|
const worker = new FFprobeWorker();
|
||||||
|
|
||||||
|
const fileInfo = await worker.getFileInfo('file.mp4');
|
||||||
|
console.log(fileInfo);
|
||||||
|
```
|
||||||
|
|
||||||
|
Browser
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { FFprobeWorker } from 'ffprobe-wasm/browser.mjs';
|
||||||
|
|
||||||
|
const worker = new FFprobeWorker();
|
||||||
|
|
||||||
|
// input is the reference to a <input type="file" /> element
|
||||||
|
input.addEventListener('change', (event) => {
|
||||||
|
const file = event.target.files[0]
|
||||||
|
const fileInfo = await worker.getFileInfo(file);
|
||||||
|
console.log(fileInfo);
|
||||||
|
});
|
||||||
|
```
|
||||||
22
build.sh
Executable file
22
build.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#/bin/sh
|
||||||
|
|
||||||
|
# Exit on error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Clean
|
||||||
|
rm -rf dist
|
||||||
|
rm -rf ffprobe-wasm-app/dist
|
||||||
|
|
||||||
|
# Build wasm
|
||||||
|
cd ffprobe-wasm-app
|
||||||
|
# sed -i -e 's/ffprobe-wasm\.js/ffprobe-wasm.mjs/g;s/-o dist\/ffprobe-wasm\.mjs \\/-o dist\/ffprobe-wasm.mjs -s EXPORT_NAME=ffprobe \\/' Makefile
|
||||||
|
docker-compose run ffprobe-wasm make
|
||||||
|
cd ..
|
||||||
|
cp -R ffprobe-wasm-app/dist dist
|
||||||
|
cp src/*.d.* dist
|
||||||
|
|
||||||
|
# Build browser/node workers
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Copy package.json
|
||||||
|
cp package.json dist
|
||||||
1
ffprobe-wasm-app
Submodule
1
ffprobe-wasm-app
Submodule
Submodule ffprobe-wasm-app added at ff36b01373
50
package-lock.json
generated
Normal file
50
package-lock.json
generated
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "ffprobe-wasm",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "ffprobe-wasm",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^17.0.21",
|
||||||
|
"typescript": "^4.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "17.0.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
|
||||||
|
"integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
|
||||||
|
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "17.0.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
|
||||||
|
"integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "4.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
|
||||||
|
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "ffprobe-wasm",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "ffprobe-like for browser and node, powered by WebAssembly",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tfoxy/ffprobe-wasm"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/tfoxy/ffprobe-wasm/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ffprobe",
|
||||||
|
"WebAssembly",
|
||||||
|
"ffmpeg",
|
||||||
|
"video"
|
||||||
|
],
|
||||||
|
"author": "Tomás Fox <tomas.c.fox@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^17.0.21",
|
||||||
|
"typescript": "^4.5.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/browser.mts
Normal file
49
src/browser.mts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { Chapter, ChapterTag, FileInfo, Frame, FramesInfo, Stream } from "./ffprobe-wasm.mjs";
|
||||||
|
import type {
|
||||||
|
IncomingMessage,
|
||||||
|
IncomingData,
|
||||||
|
OutgoingMessage,
|
||||||
|
} from "./worker.mjs";
|
||||||
|
|
||||||
|
export class FFprobeWorker {
|
||||||
|
readonly #worker: Worker;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#worker = new Worker("./worker-browser.mjs");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileInfo(file: File): Promise<FileInfo> {
|
||||||
|
return this.#postMessage({ type: "getFileInfo", payload: [file.name, { files: [file] }] });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFrames(file: File, offset: number): Promise<FramesInfo> {
|
||||||
|
return this.#postMessage({ type: "getFrames", payload: [file.name, { files: [file] }, offset] });
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate(): void {
|
||||||
|
this.#worker.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#postMessage(data: IncomingData): Promise<any> {
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
const message: IncomingMessage = {
|
||||||
|
...data,
|
||||||
|
port: channel.port2
|
||||||
|
};
|
||||||
|
|
||||||
|
this.#worker.postMessage(message, [channel.port2]);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
channel.port1.onmessage = (event: MessageEvent<OutgoingMessage>) => {
|
||||||
|
const { data } = event;
|
||||||
|
if (data.status === "success") {
|
||||||
|
resolve(data.payload);
|
||||||
|
} else {
|
||||||
|
reject(new Error(data.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Chapter, ChapterTag, FileInfo, Frame, FramesInfo, Stream };
|
||||||
145
src/ffprobe-wasm-shared.d.ts
vendored
Normal file
145
src/ffprobe-wasm-shared.d.ts
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
export interface FFprobe {
|
||||||
|
get_file_info(path: string): Raw<FileInfo>;
|
||||||
|
get_frames(path: string, offset: number): Raw<FramesInfo>;
|
||||||
|
FS: FS;
|
||||||
|
avutil_version(): string;
|
||||||
|
avcodec_version(): string;
|
||||||
|
avformat_version(): string;
|
||||||
|
onRuntimeInitialized(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FSMountOptions = WorkerFSMountOptions | NodeFSMountOptions;
|
||||||
|
|
||||||
|
export interface WorkerFSMountOptions {
|
||||||
|
files?: File[];
|
||||||
|
blobs?: Blob[];
|
||||||
|
packages?: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeFSMountOptions {
|
||||||
|
root: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FSFilesystemMountOptions {
|
||||||
|
type: FSFilesystem;
|
||||||
|
opts: FSFilesystemMountOptions;
|
||||||
|
mountpoint: string;
|
||||||
|
mounts: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FSFilesystem {
|
||||||
|
mount(opts: FSFilesystemMountOptions): FSNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FSNode {
|
||||||
|
contents: Record<string, FSNode>;
|
||||||
|
id: number;
|
||||||
|
mode: number;
|
||||||
|
name: string;
|
||||||
|
parent: FSNode;
|
||||||
|
timestamp: number;
|
||||||
|
isDevice(): boolean;
|
||||||
|
isFolder(): boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FSFilesystems {
|
||||||
|
MEMFS: FSFilesystem;
|
||||||
|
WORKERFS: FSFilesystem;
|
||||||
|
NODEFS: FSFilesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FS {
|
||||||
|
analyzePath(path: string, dontResolveLastLink?: boolean): AnalyzePathReturn;
|
||||||
|
mkdir(path: string, mode?: number): number;
|
||||||
|
mount(type: FSFilesystem, opts: FSMountOptions, mountpoint: string): FSNode;
|
||||||
|
unmount(mountpoint: string): void;
|
||||||
|
filesystems: FSFilesystems;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnalyzePathReturn {
|
||||||
|
isRoot: boolean;
|
||||||
|
exists: boolean;
|
||||||
|
error: number;
|
||||||
|
name: string | null;
|
||||||
|
path: string | null;
|
||||||
|
object: any | null;
|
||||||
|
parentExists: boolean;
|
||||||
|
parentPath: string | null;
|
||||||
|
parentObject: any | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileInfo {
|
||||||
|
bit_rate: number
|
||||||
|
chapters: Chapter[]
|
||||||
|
duration: number
|
||||||
|
flags: number
|
||||||
|
name: string
|
||||||
|
nb_chapters: number
|
||||||
|
nb_streams: number
|
||||||
|
streams: Stream[]
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Collection<T> {
|
||||||
|
count: { value: number }
|
||||||
|
ptr: number;
|
||||||
|
ptrType: any;
|
||||||
|
get(index: number): T;
|
||||||
|
size(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Raw<T> = {
|
||||||
|
[K in keyof T]: T[K] extends Array<infer U> ? Collection<Raw<U>> : T[K]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Chapter {
|
||||||
|
end: number
|
||||||
|
id: number
|
||||||
|
start: number
|
||||||
|
tags: ChapterTag[]
|
||||||
|
/**
|
||||||
|
* @example "1/1000"
|
||||||
|
*/
|
||||||
|
time_base: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChapterTag {
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Stream {
|
||||||
|
bit_rate: number
|
||||||
|
channels: number
|
||||||
|
codec_name: string
|
||||||
|
codec_type: number
|
||||||
|
duration: number
|
||||||
|
format: string
|
||||||
|
frame_size: number
|
||||||
|
height: number
|
||||||
|
id: number
|
||||||
|
level: number
|
||||||
|
profile: string
|
||||||
|
sample_rate: number
|
||||||
|
start_time: number
|
||||||
|
width: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FramesInfo {
|
||||||
|
avg_frame_rate: number
|
||||||
|
duration: number
|
||||||
|
frames: Frame[]
|
||||||
|
gop_size: number
|
||||||
|
nb_frames: number
|
||||||
|
time_base: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Frame {
|
||||||
|
dts: number
|
||||||
|
frame_number: number
|
||||||
|
pict_type: number
|
||||||
|
pkt_size: number
|
||||||
|
pos: number
|
||||||
|
pts: number
|
||||||
|
}
|
||||||
9
src/ffprobe-wasm.d.mts
Normal file
9
src/ffprobe-wasm.d.mts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { FFprobe } from "./ffprobe-wasm-shared";
|
||||||
|
|
||||||
|
export * from "./ffprobe-wasm-shared";
|
||||||
|
|
||||||
|
export default function loadFFprobe(ffprobe?: FFprobeInit): Promise<FFprobe>;
|
||||||
|
|
||||||
|
export interface FFprobeInit {
|
||||||
|
locateFile?(path: string, scriptDirectory: string): string;
|
||||||
|
}
|
||||||
7
src/ffprobe-wasm.d.ts
vendored
Normal file
7
src/ffprobe-wasm.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { FFprobe } from "./ffprobe-wasm-shared";
|
||||||
|
|
||||||
|
export * from "./ffprobe-wasm-shared";
|
||||||
|
|
||||||
|
declare const ffprobe: FFprobe;
|
||||||
|
|
||||||
|
export default ffprobe;
|
||||||
52
src/node.mts
Normal file
52
src/node.mts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { basename, dirname } from "path";
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { MessageChannel, Worker } from "worker_threads";
|
||||||
|
import type { Chapter, ChapterTag, FileInfo, Frame, FramesInfo, Stream } from "./ffprobe-wasm.mjs";
|
||||||
|
import type {
|
||||||
|
IncomingMessage,
|
||||||
|
IncomingData,
|
||||||
|
OutgoingMessage,
|
||||||
|
} from "./worker.mjs";
|
||||||
|
|
||||||
|
export class FFprobeWorker {
|
||||||
|
readonly #worker: Worker;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
this.#worker = new Worker(`${__dirname}/worker-node.mjs`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileInfo(filePath: string): Promise<FileInfo> {
|
||||||
|
return this.#postMessage({ type: "getFileInfo", payload: [basename(filePath), { root: dirname(filePath) }] });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFrames(filePath: string, offset: number): Promise<FramesInfo> {
|
||||||
|
return this.#postMessage({ type: "getFrames", payload: [basename(filePath), { root: dirname(filePath) }, offset] });
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate(): void {
|
||||||
|
this.#worker.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#postMessage(data: IncomingData): Promise<any> {
|
||||||
|
const channel = new MessageChannel();
|
||||||
|
const message: IncomingMessage = {
|
||||||
|
...data,
|
||||||
|
port: channel.port2
|
||||||
|
};
|
||||||
|
|
||||||
|
this.#worker.postMessage(message, [channel.port2]);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
channel.port1.on("message", (data: OutgoingMessage) => {
|
||||||
|
if (data.status === "success") {
|
||||||
|
resolve(data.payload);
|
||||||
|
} else {
|
||||||
|
reject(new Error(data.message));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Chapter, ChapterTag, FileInfo, Frame, FramesInfo, Stream };
|
||||||
11
src/worker-browser.mts
Normal file
11
src/worker-browser.mts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import loadFFprobe from "./ffprobe-wasm.mjs";
|
||||||
|
import { createListener, IncomingMessage } from "./worker.mjs";
|
||||||
|
|
||||||
|
const listener = createListener(
|
||||||
|
loadFFprobe({
|
||||||
|
locateFile: (path) => `${location.origin}/node_modules/ffprobe-wasm/${path}`
|
||||||
|
}),
|
||||||
|
"WORKERFS",
|
||||||
|
);
|
||||||
|
|
||||||
|
self.onmessage = (event: MessageEvent<IncomingMessage>) => listener(event.data);
|
||||||
19
src/worker-node.mts
Normal file
19
src/worker-node.mts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { createRequire } from "module";
|
||||||
|
import { parentPort } from "worker_threads";
|
||||||
|
import type { FFprobe } from "./ffprobe-wasm.js";
|
||||||
|
import { createListener } from "./worker.mjs";
|
||||||
|
|
||||||
|
if (!parentPort) {
|
||||||
|
throw new Error("parentPort must be defined. Are you sure you are in a worker context?");
|
||||||
|
}
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const listener = createListener(new Promise((resolve) => {
|
||||||
|
const ffprobe: FFprobe = require('./ffprobe-wasm.js')
|
||||||
|
ffprobe.onRuntimeInitialized = () => {
|
||||||
|
resolve(ffprobe);
|
||||||
|
}
|
||||||
|
}), "NODEFS");
|
||||||
|
|
||||||
|
parentPort.on("message", listener);
|
||||||
133
src/worker.mts
Normal file
133
src/worker.mts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import type { MessagePort as NodeMessagePort } from "worker_threads";
|
||||||
|
import type { Chapter, ChapterTag, FFprobe, FileInfo, FramesInfo, FSFilesystems, FSMountOptions, Stream } from "./ffprobe-wasm-shared";
|
||||||
|
|
||||||
|
export type IncomingMessage = { port: MessagePort | NodeMessagePort } & IncomingData;
|
||||||
|
|
||||||
|
export type IncomingData =
|
||||||
|
| {
|
||||||
|
type: "getFileInfo";
|
||||||
|
payload: [fileName: string, mountOptions: FSMountOptions];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "getFrames";
|
||||||
|
payload: [fileName: string, mountOptions: FSMountOptions, offset: number];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OutgoingMessage =
|
||||||
|
| ({ status: "success" } & OutgoingData)
|
||||||
|
| { status: "error"; message: string };
|
||||||
|
|
||||||
|
export type OutgoingData =
|
||||||
|
| {
|
||||||
|
type: "getFileInfo";
|
||||||
|
payload: FileInfo;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "getFrames";
|
||||||
|
payload: FramesInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createListener(
|
||||||
|
ffprobePromise: Promise<FFprobe>,
|
||||||
|
fsType: keyof FSFilesystems,
|
||||||
|
) {
|
||||||
|
return onmessage;
|
||||||
|
|
||||||
|
async function onmessage(data: IncomingMessage) {
|
||||||
|
try {
|
||||||
|
switch (data.type) {
|
||||||
|
case "getFileInfo":
|
||||||
|
data.port.postMessage({
|
||||||
|
status: "success",
|
||||||
|
payload: await getFileInfo(...data.payload),
|
||||||
|
type: data.type,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "getFrames":
|
||||||
|
data.port.postMessage({
|
||||||
|
status: "success",
|
||||||
|
payload: await getFrames(...data.payload),
|
||||||
|
type: data.type,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const _: never = data;
|
||||||
|
throw new Error(`Unknown event: ${JSON.stringify(_)}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
data.port.postMessage({
|
||||||
|
status: "error",
|
||||||
|
message: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFileInfo(fileName: string, mountOptions: FSMountOptions): Promise<FileInfo> {
|
||||||
|
const { FS, get_file_info } = await ffprobePromise;
|
||||||
|
try {
|
||||||
|
if (!FS.analyzePath("/work").exists) {
|
||||||
|
FS.mkdir("/work");
|
||||||
|
}
|
||||||
|
FS.mount(FS.filesystems[fsType], mountOptions, "/work");
|
||||||
|
|
||||||
|
// Call the wasm module.
|
||||||
|
const rawInfo = get_file_info(`/work/${fileName}`);
|
||||||
|
|
||||||
|
// Remap streams into collection.
|
||||||
|
const streams: Stream[] = [];
|
||||||
|
for (let i = 0; i < rawInfo.streams.size(); i++) {
|
||||||
|
streams.push(rawInfo.streams.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remap chapters into collection.
|
||||||
|
const chapters: Chapter[] = [];
|
||||||
|
for (let i = 0; i < rawInfo.chapters.size(); i++) {
|
||||||
|
const rawChapter = rawInfo.chapters.get(i);
|
||||||
|
|
||||||
|
const tags: ChapterTag[] = [];
|
||||||
|
for (let j = 0; j < rawChapter.tags.size(); j++) {
|
||||||
|
tags.push(rawChapter.tags.get(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
chapters.push({ ...rawChapter, tags });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rawInfo,
|
||||||
|
streams,
|
||||||
|
chapters,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
// Cleanup mount.
|
||||||
|
FS.unmount("/work");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFrames(fileName: string, mountOptions: FSMountOptions, offset: number): Promise<FramesInfo> {
|
||||||
|
const { FS, get_frames } = await ffprobePromise;
|
||||||
|
try {
|
||||||
|
if (!FS.analyzePath("/work").exists) {
|
||||||
|
FS.mkdir("/work");
|
||||||
|
}
|
||||||
|
FS.mount(FS.filesystems.WORKERFS, mountOptions, "/work");
|
||||||
|
|
||||||
|
// Call the wasm module.
|
||||||
|
const framesInfo = get_frames(`/work/${fileName}`, offset);
|
||||||
|
|
||||||
|
// Remap frames into collection.
|
||||||
|
const frames = [];
|
||||||
|
for (let i = 0; i < framesInfo.frames.size(); i++) {
|
||||||
|
frames.push(framesInfo.frames.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...framesInfo,
|
||||||
|
frames,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
// Cleanup mount.
|
||||||
|
FS.unmount("/work");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2021",
|
||||||
|
"lib": ["DOM", "ES2021"],
|
||||||
|
"module": "ES2020",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowJs": false,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user