mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-28 11:23:45 +02:00
Merge main into release for tag v1.41.0
This commit is contained in:
Generated
+9
-9
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mediabunny",
|
"name": "mediabunny",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mediabunny",
|
"name": "mediabunny",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@@ -7751,9 +7751,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mediabunny": {
|
"node_modules/mediabunny": {
|
||||||
"version": "1.40.0",
|
"version": "1.40.1",
|
||||||
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.40.1.tgz",
|
||||||
"integrity": "sha512-UztWnjkA15yYxqq7AC8MSsU4U6FIFIBHvHV94pouvjxQa0y2pGvi1HPmrFcaYrRZAuSKTrdw45VXUIe3gEEpsA==",
|
"integrity": "sha512-HU/stGzAkdWaJIly6ypbUVgAUvT9kt39DIg0IaErR7/1fwtTmgUYs4i8uEPYcgcjPjbB9gtBmUXOLnXi6J2LDw==",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@@ -12077,7 +12077,7 @@
|
|||||||
},
|
},
|
||||||
"packages/aac-encoder": {
|
"packages/aac-encoder": {
|
||||||
"name": "@mediabunny/aac-encoder",
|
"name": "@mediabunny/aac-encoder",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/emscripten": "^1.40.1"
|
"@types/emscripten": "^1.40.1"
|
||||||
@@ -12092,7 +12092,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ac3": {
|
"packages/ac3": {
|
||||||
"name": "@mediabunny/ac3",
|
"name": "@mediabunny/ac3",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/emscripten": "^1.40.1"
|
"@types/emscripten": "^1.40.1"
|
||||||
@@ -12107,7 +12107,7 @@
|
|||||||
},
|
},
|
||||||
"packages/flac-encoder": {
|
"packages/flac-encoder": {
|
||||||
"name": "@mediabunny/flac-encoder",
|
"name": "@mediabunny/flac-encoder",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/emscripten": "^1.40.1"
|
"@types/emscripten": "^1.40.1"
|
||||||
@@ -12122,7 +12122,7 @@
|
|||||||
},
|
},
|
||||||
"packages/mp3-encoder": {
|
"packages/mp3-encoder": {
|
||||||
"name": "@mediabunny/mp3-encoder",
|
"name": "@mediabunny/mp3-encoder",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"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.40.1",
|
"version": "1.41.0",
|
||||||
"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": [
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ const output = new Output({
|
|||||||
const conversion = await Conversion.init({
|
const conversion = await Conversion.init({
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
|
audio: {
|
||||||
|
codec: 'aac',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
await conversion.execute();
|
await conversion.execute();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mediabunny/aac-encoder",
|
"name": "@mediabunny/aac-encoder",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"description": "AAC encoder extension for Mediabunny, based on FFmpeg.",
|
"description": "AAC encoder extension for Mediabunny, based on FFmpeg.",
|
||||||
"main": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
"main": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||||
"module": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
"module": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mediabunny/ac3",
|
"name": "@mediabunny/ac3",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on FFmpeg.",
|
"description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on FFmpeg.",
|
||||||
"main": "./dist/bundles/mediabunny-ac3.mjs",
|
"main": "./dist/bundles/mediabunny-ac3.mjs",
|
||||||
"module": "./dist/bundles/mediabunny-ac3.mjs",
|
"module": "./dist/bundles/mediabunny-ac3.mjs",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mediabunny/flac-encoder",
|
"name": "@mediabunny/flac-encoder",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"description": "FLAC encoder extension for Mediabunny, based on libFLAC.",
|
"description": "FLAC encoder extension for Mediabunny, based on libFLAC.",
|
||||||
"main": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
"main": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||||
"module": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
"module": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@mediabunny/mp3-encoder",
|
"name": "@mediabunny/mp3-encoder",
|
||||||
"author": "Vanilagy",
|
"author": "Vanilagy",
|
||||||
"version": "1.40.1",
|
"version": "1.41.0",
|
||||||
"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",
|
||||||
|
|||||||
+54
-7
@@ -16,6 +16,7 @@ import {
|
|||||||
assertNever,
|
assertNever,
|
||||||
keyValueIterator,
|
keyValueIterator,
|
||||||
toDataView,
|
toDataView,
|
||||||
|
isRecordStringString,
|
||||||
} from './misc';
|
} from './misc';
|
||||||
import { FileSlice, readAscii, readBytes, readU32Be, readU8 } from './reader';
|
import { FileSlice, readAscii, readBytes, readU32Be, readU8 } from './reader';
|
||||||
import { Writer } from './writer';
|
import { Writer } from './writer';
|
||||||
@@ -225,7 +226,14 @@ export const parseId3V2Tag = (slice: FileSlice, header: Id3V2Header, tags: Metad
|
|||||||
}
|
}
|
||||||
|
|
||||||
tags.raw ??= {};
|
tags.raw ??= {};
|
||||||
if (frame.id[0] === 'T') {
|
if (frame.id === 'TXXX') {
|
||||||
|
const txxx = tags.raw['TXXX'] ??= {};
|
||||||
|
const encoding = reader.readId3V2TextEncoding();
|
||||||
|
const description = reader.readId3V2Text(encoding, frameEndPos);
|
||||||
|
const value = reader.readId3V2Text(encoding, frameEndPos);
|
||||||
|
|
||||||
|
(txxx as Record<string, string>)[description] ??= value;
|
||||||
|
} else if (frame.id[0] === 'T') {
|
||||||
// It's a text frame, let's decode as text
|
// It's a text frame, let's decode as text
|
||||||
tags.raw[frame.id] ??= reader.readId3V2EncodingAndText(frameEndPos);
|
tags.raw[frame.id] ??= reader.readId3V2EncodingAndText(frameEndPos);
|
||||||
} else {
|
} else {
|
||||||
@@ -761,13 +769,51 @@ export class Id3V2Writer {
|
|||||||
|
|
||||||
let bytes: Uint8Array;
|
let bytes: Uint8Array;
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
const encoded = textEncoder.encode(value);
|
const useIso88591 = isIso88591Compatible(value);
|
||||||
bytes = new Uint8Array(encoded.byteLength + 2);
|
if (useIso88591) {
|
||||||
bytes[0] = Id3V2TextEncoding.UTF_8;
|
bytes = new Uint8Array(value.length + 2);
|
||||||
bytes.set(encoded, 1);
|
bytes[0] = Id3V2TextEncoding.ISO_8859_1;
|
||||||
// Last byte is the null terminator
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
bytes[i + 1] = value.charCodeAt(i);
|
||||||
|
}
|
||||||
|
// Last byte is the null terminator
|
||||||
|
} else {
|
||||||
|
const encoded = textEncoder.encode(value);
|
||||||
|
bytes = new Uint8Array(encoded.byteLength + 2);
|
||||||
|
bytes[0] = Id3V2TextEncoding.UTF_8;
|
||||||
|
bytes.set(encoded, 1);
|
||||||
|
// Last byte is the null terminator
|
||||||
|
}
|
||||||
} else if (value instanceof Uint8Array) {
|
} else if (value instanceof Uint8Array) {
|
||||||
bytes = value;
|
bytes = value;
|
||||||
|
} else if (key === 'TXXX' && isRecordStringString(value)) {
|
||||||
|
for (const description in value) {
|
||||||
|
const frameValue = value[description]!;
|
||||||
|
const useIso88591 = isIso88591Compatible(description) && isIso88591Compatible(frameValue);
|
||||||
|
const descriptionDataLength = useIso88591
|
||||||
|
? description.length
|
||||||
|
: textEncoder.encode(description).byteLength;
|
||||||
|
|
||||||
|
const valueData = useIso88591 ? null : textEncoder.encode(frameValue);
|
||||||
|
const valueDataLength = useIso88591 ? frameValue.length : valueData!.byteLength;
|
||||||
|
const frameSize = 1 + descriptionDataLength + 1 + valueDataLength;
|
||||||
|
|
||||||
|
this.writeAscii('TXXX');
|
||||||
|
this.writeSynchsafeU32(frameSize);
|
||||||
|
this.writeU16(0x0000);
|
||||||
|
|
||||||
|
this.writeU8(useIso88591 ? Id3V2TextEncoding.ISO_8859_1 : Id3V2TextEncoding.UTF_8);
|
||||||
|
if (useIso88591) {
|
||||||
|
this.writeIsoString(description);
|
||||||
|
for (let i = 0; i < frameValue.length; i++) {
|
||||||
|
this.writeU8(frameValue.charCodeAt(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.writeUtf8String(description);
|
||||||
|
this.writer.write(valueData!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -821,7 +867,8 @@ export class Id3V2Writer {
|
|||||||
for (let i = 0; i < text.length; i++) {
|
for (let i = 0; i < text.length; i++) {
|
||||||
bytes[i] = text.charCodeAt(i);
|
bytes[i] = text.charCodeAt(i);
|
||||||
}
|
}
|
||||||
bytes[text.length] = 0x00;
|
// Last byte is the null terminator
|
||||||
|
|
||||||
this.writer.write(bytes);
|
this.writer.write(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -6,6 +6,8 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { isRecordStringString } from './misc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents descriptive (non-technical) metadata about a media file, such as title, author, date, cover art, or other
|
* Represents descriptive (non-technical) metadata about a media file, such as title, author, date, cover art, or other
|
||||||
* attached files. Common tags are normalized by Mediabunny into a uniform format, while the `raw` field can be used to
|
* attached files. Common tags are normalized by Mediabunny into a uniform format, while the `raw` field can be used to
|
||||||
@@ -69,8 +71,9 @@ export type MetadataTags = {
|
|||||||
* - WebM/Matroska: `SimpleTag` elements whose target is 50 (MOVIE), either containing string or `Uint8Array`
|
* - WebM/Matroska: `SimpleTag` elements whose target is 50 (MOVIE), either containing string or `Uint8Array`
|
||||||
* values. Additionally, all attached files (such as font files) are included here, where the key corresponds to
|
* values. Additionally, all attached files (such as font files) are included here, where the key corresponds to
|
||||||
* the FileUID and the value is an {@link AttachedFile}.
|
* the FileUID and the value is an {@link AttachedFile}.
|
||||||
* - MP3: The ID3v2 tags, or a single `'TAG'` key with the contents of the ID3v1 tag.
|
* - MP3: The ID3v2 tags, or a single `'TAG'` key with the contents of the ID3v1 tag. The ID3v2 `'TXXX'`
|
||||||
* - ADTS: The ID3v2 tags.
|
* user-defined text frames are exposed as a `Record<string, string>`.
|
||||||
|
* - ADTS: The ID3v2 tags, just like in MP3.
|
||||||
* - Ogg: The key-value string pairs from the Vorbis-style comment header (see RFC 7845, Section 5.2).
|
* - Ogg: The key-value string pairs from the Vorbis-style comment header (see RFC 7845, Section 5.2).
|
||||||
* Additionally, the `'vendor'` key refers to the vendor string within this header.
|
* Additionally, the `'vendor'` key refers to the vendor string within this header.
|
||||||
* - WAVE: The individual metadata chunks within the RIFF INFO chunk. Values are always ISO 8859-1 strings.
|
* - WAVE: The individual metadata chunks within the RIFF INFO chunk. Values are always ISO 8859-1 strings.
|
||||||
@@ -78,7 +81,7 @@ export type MetadataTags = {
|
|||||||
* Additionally, the `'vendor'` key refers to the vendor string within this header.
|
* Additionally, the `'vendor'` key refers to the vendor string within this header.
|
||||||
* - MPEG-TS: Not supported.
|
* - MPEG-TS: Not supported.
|
||||||
*/
|
*/
|
||||||
raw?: Record<string, string | Uint8Array | RichImageData | AttachedFile | null>;
|
raw?: Record<string, string | Uint8Array | RichImageData | AttachedFile | Record<string, string> | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,9 +239,11 @@ export const validateMetadataTags = (tags: MetadataTags) => {
|
|||||||
&& !(value instanceof Uint8Array)
|
&& !(value instanceof Uint8Array)
|
||||||
&& !(value instanceof RichImageData)
|
&& !(value instanceof RichImageData)
|
||||||
&& !(value instanceof AttachedFile)
|
&& !(value instanceof AttachedFile)
|
||||||
|
&& !isRecordStringString(value)
|
||||||
) {
|
) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
'Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, or null.',
|
'Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, '
|
||||||
|
+ 'Record<string, string>, or null.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1009,3 +1009,10 @@ export const clearIntervalUnthrottled = (timer: UnthrottledTimerHandle) => {
|
|||||||
timerId: timer.id,
|
timerId: timer.id,
|
||||||
} satisfies UnthrottledTimerMessage);
|
} satisfies UnthrottledTimerMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isRecordStringString = (value: unknown): value is Record<string, string> => {
|
||||||
|
return value !== null
|
||||||
|
&& typeof value === 'object'
|
||||||
|
&& Object.getPrototypeOf(value) === Object.prototype
|
||||||
|
&& Object.values(value).every(x => typeof x === 'string');
|
||||||
|
};
|
||||||
|
|||||||
@@ -338,6 +338,12 @@ test('Read and write metadata, MP3', async () => {
|
|||||||
...songMetadata,
|
...songMetadata,
|
||||||
raw: {
|
raw: {
|
||||||
TXXY: 'ID3v2 goated',
|
TXXY: 'ID3v2 goated',
|
||||||
|
TXXZ: '鱼',
|
||||||
|
TXXX: {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux',
|
||||||
|
你: '好',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -377,6 +383,12 @@ test('Read and write metadata, MP3', async () => {
|
|||||||
expect(readTags.raw!['TIT2']).toBe(songMetadata.title);
|
expect(readTags.raw!['TIT2']).toBe(songMetadata.title);
|
||||||
expect(readTags.raw!['APIC']).instanceOf(Uint8Array);
|
expect(readTags.raw!['APIC']).instanceOf(Uint8Array);
|
||||||
expect(readTags.raw!['TXXY']).toBe('ID3v2 goated');
|
expect(readTags.raw!['TXXY']).toBe('ID3v2 goated');
|
||||||
|
expect(readTags.raw!['TXXZ']).toBe('鱼');
|
||||||
|
expect(readTags.raw!['TXXX']).toEqual({
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux',
|
||||||
|
你: '好',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Read and write metadata, Ogg', async () => {
|
test('Read and write metadata, Ogg', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user