mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Merge main into release for tag v1.25.2
This commit is contained in:
@@ -14,6 +14,9 @@ export default withMermaid({
|
|||||||
title: 'Mediabunny',
|
title: 'Mediabunny',
|
||||||
description: DESCRIPTION,
|
description: DESCRIPTION,
|
||||||
cleanUrls: true,
|
cleanUrls: true,
|
||||||
|
sitemap: {
|
||||||
|
hostname: 'https://mediabunny.dev',
|
||||||
|
},
|
||||||
head: [
|
head: [
|
||||||
['link', { rel: 'icon', type: 'image/png', href: '/mediabunny-logo.png' }],
|
['link', { rel: 'icon', type: 'image/png', href: '/mediabunny-logo.png' }],
|
||||||
['link', { rel: 'icon', type: 'image/svg+xml', href: '/mediabunny-logo.svg' }],
|
['link', { rel: 'icon', type: 'image/svg+xml', href: '/mediabunny-logo.svg' }],
|
||||||
@@ -34,7 +37,7 @@ export default withMermaid({
|
|||||||
// https://vitepress.dev/reference/default-theme-config
|
// https://vitepress.dev/reference/default-theme-config
|
||||||
nav: [
|
nav: [
|
||||||
{ text: 'Guide', link: '/guide/introduction', activeMatch: '/guide' },
|
{ text: 'Guide', link: '/guide/introduction', activeMatch: '/guide' },
|
||||||
{ text: 'API', link: '/api', activeMatch: '/api' },
|
{ text: 'API', link: '/api/', activeMatch: '/api' },
|
||||||
{ text: 'LLMs', link: '/llms', activeMatch: '/llms' },
|
{ text: 'LLMs', link: '/llms', activeMatch: '/llms' },
|
||||||
{ text: 'Examples', link: '/examples', activeMatch: '/examples' },
|
{ text: 'Examples', link: '/examples', activeMatch: '/examples' },
|
||||||
{ text: 'Sponsors', link: '/#sponsors', activeMatch: '/#sponsors' },
|
{ text: 'Sponsors', link: '/#sponsors', activeMatch: '/#sponsors' },
|
||||||
|
|||||||
+6
-6
@@ -9,37 +9,37 @@ hero:
|
|||||||
features:
|
features:
|
||||||
- title: Metadata extraction
|
- title: Metadata extraction
|
||||||
details: Extract various metadata from an input media file.
|
details: Extract various metadata from an input media file.
|
||||||
link: /examples/metadata-extraction
|
link: /examples/metadata-extraction/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--file-info-line.svg
|
src: /mingcute--file-info-line.svg
|
||||||
- title: Thumbnail generation
|
- title: Thumbnail generation
|
||||||
details: Generate multiple small thumbnails for a video track.
|
details: Generate multiple small thumbnails for a video track.
|
||||||
link: /examples/thumbnail-generation
|
link: /examples/thumbnail-generation/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--photo-album-line.svg
|
src: /mingcute--photo-album-line.svg
|
||||||
- title: Media player (advanced)
|
- title: Media player (advanced)
|
||||||
details: "A full video & audio media player, implemented from scratch with Mediabunny, with microsecond playback accuracy."
|
details: "A full video & audio media player, implemented from scratch with Mediabunny, with microsecond playback accuracy."
|
||||||
link: /examples/media-player
|
link: /examples/media-player/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--video-line.svg
|
src: /mingcute--video-line.svg
|
||||||
- title: File compression
|
- title: File compression
|
||||||
details: Convert an input file to a highly-compressed MP4 file.
|
details: Convert an input file to a highly-compressed MP4 file.
|
||||||
link: /examples/file-compression
|
link: /examples/file-compression/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--file-zip-line.svg
|
src: /mingcute--file-zip-line.svg
|
||||||
- title: Procedural video generation
|
- title: Procedural video generation
|
||||||
details: Generate a video file as fast as the hardware allows.
|
details: Generate a video file as fast as the hardware allows.
|
||||||
link: /examples/procedural-generation
|
link: /examples/procedural-generation/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--magic-3-line.svg
|
src: /mingcute--magic-3-line.svg
|
||||||
- title: Live recording & streaming
|
- title: Live recording & streaming
|
||||||
details: Record a video from live sources and stream it to a video element.
|
details: Record a video from live sources and stream it to a video element.
|
||||||
link: /examples/live-recording
|
link: /examples/live-recording/
|
||||||
target: _self
|
target: _self
|
||||||
icon:
|
icon:
|
||||||
src: /mingcute--microphone-line.svg
|
src: /mingcute--microphone-line.svg
|
||||||
|
|||||||
Generated
+6
-6
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mediabunny",
|
"name": "mediabunny",
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mediabunny",
|
"name": "mediabunny",
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@@ -7749,9 +7749,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mediabunny": {
|
"node_modules/mediabunny": {
|
||||||
"version": "1.25.0",
|
"version": "1.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.25.1.tgz",
|
||||||
"integrity": "sha512-ozaqk6zS2Vbf3+3+OoxKfnCVeZRcv5PO8DgQtBrM5vpWIbpEK+kMVV6pgfo4mC3XtMwvQEMbhj3zEf0LNklh9w==",
|
"integrity": "sha512-0Rrd47PMCVJbTPA7IJaXPCupV5/RZ/icgr+a0qExRJAr0n5vB4fsGSo+fdHIehG0CrddXtVRvNZwFtJz709yfA==",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@@ -12242,7 +12242,7 @@
|
|||||||
},
|
},
|
||||||
"packages/mp3-encoder": {
|
"packages/mp3-encoder": {
|
||||||
"name": "@mediabunny/mp3-encoder",
|
"name": "@mediabunny/mp3-encoder",
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/emscripten": "^1.40.1"
|
"@types/emscripten": "^1.40.1"
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "mediabunny",
|
"name": "mediabunny",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
|
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mediabunny/mp3-encoder",
|
"name": "@mediabunny/mp3-encoder",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.25.1",
|
"version": "1.25.2",
|
||||||
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
|
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
|
||||||
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||||
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||||
|
|||||||
+40
-1
@@ -22,6 +22,7 @@ import {
|
|||||||
VideoCodec,
|
VideoCodec,
|
||||||
} from './codec';
|
} from './codec';
|
||||||
import { customAudioEncoders, customVideoEncoders } from './custom-coder';
|
import { customAudioEncoders, customVideoEncoders } from './custom-coder';
|
||||||
|
import { isFirefox } from './misc';
|
||||||
import { EncodedPacket } from './packet';
|
import { EncodedPacket } from './packet';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -529,7 +530,45 @@ export const canEncodeVideo = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const support = await VideoEncoder.isConfigSupported(encoderConfig);
|
const support = await VideoEncoder.isConfigSupported(encoderConfig);
|
||||||
return support.supported === true;
|
if (!support.supported) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFirefox()) {
|
||||||
|
// isConfigSupported on Firefox appears to unreliably indicate if encoding will actually succeed. Therefore, we
|
||||||
|
// just try encoding a frame to see if it actually works.
|
||||||
|
// https://github.com/Vanilagy/mediabunny/issues/222
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises, no-async-promise-executor
|
||||||
|
return new Promise<boolean>(async (resolve) => {
|
||||||
|
try {
|
||||||
|
const encoder = new VideoEncoder({
|
||||||
|
output: () => {},
|
||||||
|
error: () => resolve(false),
|
||||||
|
});
|
||||||
|
encoder.configure(encoderConfig);
|
||||||
|
|
||||||
|
const frameData = new Uint8Array(width * height * 4);
|
||||||
|
const frame = new VideoFrame(frameData, {
|
||||||
|
format: 'RGBA',
|
||||||
|
codedWidth: width,
|
||||||
|
codedHeight: height,
|
||||||
|
timestamp: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
encoder.encode(frame);
|
||||||
|
frame.close();
|
||||||
|
|
||||||
|
await encoder.flush();
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
} catch {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -584,6 +584,7 @@ export const retriedFetch = async (
|
|||||||
url: string | URL | Request,
|
url: string | URL | Request,
|
||||||
requestInit: RequestInit,
|
requestInit: RequestInit,
|
||||||
getRetryDelay: (previousAttempts: number, error: unknown, url: string | URL | Request) => number | null,
|
getRetryDelay: (previousAttempts: number, error: unknown, url: string | URL | Request) => number | null,
|
||||||
|
shouldStop: () => boolean,
|
||||||
) => {
|
) => {
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
|
|
||||||
@@ -591,6 +592,10 @@ export const retriedFetch = async (
|
|||||||
try {
|
try {
|
||||||
return await fetchFn(url, requestInit);
|
return await fetchFn(url, requestInit);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (shouldStop()) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
attempts++;
|
attempts++;
|
||||||
const retryDelayInSeconds = getRetryDelay(attempts, error, url);
|
const retryDelayInSeconds = getRetryDelay(attempts, error, url);
|
||||||
|
|
||||||
@@ -607,6 +612,10 @@ export const retriedFetch = async (
|
|||||||
if (retryDelayInSeconds > 0) {
|
if (retryDelayInSeconds > 0) {
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000 * retryDelayInSeconds));
|
await new Promise(resolve => setTimeout(resolve, 1000 * retryDelayInSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldStop()) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-1
@@ -297,6 +297,10 @@ const DEFAULT_RETRY_DELAY
|
|||||||
= typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true;
|
= typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true;
|
||||||
|
|
||||||
if (isOnline && originOfSrc !== null && originOfSrc !== window.location.origin) {
|
if (isOnline && originOfSrc !== null && originOfSrc !== window.location.origin) {
|
||||||
|
console.warn(
|
||||||
|
`Request will not be retried because a CORS error was suspected due to different origins. You can`
|
||||||
|
+ ` modify this behavior by providing your own function for the 'getRetryDelay' option.`,
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,6 +429,7 @@ export class UrlSource extends Source {
|
|||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
}),
|
}),
|
||||||
this._getRetryDelay,
|
this._getRetryDelay,
|
||||||
|
() => this._disposed,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -492,6 +497,7 @@ export class UrlSource extends Source {
|
|||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
}),
|
}),
|
||||||
this._getRetryDelay,
|
this._getRetryDelay,
|
||||||
|
() => this._disposed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,6 +545,11 @@ export class UrlSource extends Source {
|
|||||||
try {
|
try {
|
||||||
readResult = await reader.read();
|
readResult = await reader.read();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (this._disposed) {
|
||||||
|
// No need to try to retry
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
const retryDelayInSeconds = this._getRetryDelay(1, error, this._url);
|
const retryDelayInSeconds = this._getRetryDelay(1, error, this._url);
|
||||||
if (retryDelayInSeconds !== null) {
|
if (retryDelayInSeconds !== null) {
|
||||||
console.error('Error while reading response stream. Attempting to resume.', error);
|
console.error('Error while reading response stream. Attempting to resume.', error);
|
||||||
@@ -639,7 +650,7 @@ export class FilePathSource extends Source {
|
|||||||
_fileHandle: FileHandle | null = null;
|
_fileHandle: FileHandle | null = null;
|
||||||
|
|
||||||
/** Creates a new {@link FilePathSource} backed by the file at the specified file path. */
|
/** Creates a new {@link FilePathSource} backed by the file at the specified file path. */
|
||||||
constructor(filePath: string, options: BlobSourceOptions = {}) {
|
constructor(filePath: string, options: FilePathSourceOptions = {}) {
|
||||||
if (typeof filePath !== 'string') {
|
if (typeof filePath !== 'string') {
|
||||||
throw new TypeError('filePath must be a string.');
|
throw new TypeError('filePath must be a string.');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user