mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-28 03:13:51 +02:00
Add metadata tests, fix a few bugs
This commit is contained in:
+5
-1
@@ -14,7 +14,11 @@
|
||||
source: new Mediabunny.BlobSource(file),
|
||||
});
|
||||
|
||||
console.log(await input.getMetadata());
|
||||
const audioTrack = await input.getPrimaryAudioTrack();
|
||||
const sink = new Mediabunny.EncodedPacketSink(audioTrack);
|
||||
|
||||
console.log(await audioTrack.getDecoderConfig())
|
||||
console.log(await sink.getFirstPacket());
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -188,5 +188,9 @@ export {
|
||||
registerDecoder,
|
||||
registerEncoder,
|
||||
} from './custom-coder';
|
||||
export {
|
||||
MetadataTags,
|
||||
RichImageData,
|
||||
} from './tags';
|
||||
|
||||
// 🐡🦔
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ export class Input<S extends Source = Source> {
|
||||
return demuxer.getMimeType();
|
||||
}
|
||||
|
||||
async getMetadata() {
|
||||
async getMetadataTags() {
|
||||
const demuxer = await this._getDemuxer();
|
||||
return demuxer.getMetadataTags();
|
||||
}
|
||||
|
||||
@@ -1520,13 +1520,13 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
case 'part_number': {
|
||||
const parts = value.split('/');
|
||||
const trackNum = Number.parseInt(parts[0]!, 10);
|
||||
const trackNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(trackNum) && trackNum > 0) {
|
||||
metadataTags.trackNumber ??= trackNum;
|
||||
}
|
||||
if (trackNumMax && Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
metadataTags.tracksTotal ??= trackNumMax;
|
||||
if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
||||
metadataTags.tracksTotal ??= tracksTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
@@ -1534,13 +1534,13 @@ export class MatroskaDemuxer extends Demuxer {
|
||||
case 'disc': {
|
||||
const discParts = value.split('/');
|
||||
const discNum = Number.parseInt(discParts[0]!, 10);
|
||||
const discNumMax = discParts[1] && Number.parseInt(discParts[1], 10);
|
||||
const discsTotal = discParts[1] && Number.parseInt(discParts[1], 10);
|
||||
|
||||
if (Number.isInteger(discNum) && discNum > 0) {
|
||||
metadataTags.discNumber ??= discNum;
|
||||
}
|
||||
if (discNumMax && Number.isInteger(discNumMax) && discNumMax > 0) {
|
||||
metadataTags.discsTotal ??= discNumMax;
|
||||
if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) {
|
||||
metadataTags.discsTotal ??= discsTotal;
|
||||
}
|
||||
}; break;
|
||||
}
|
||||
|
||||
@@ -60,8 +60,9 @@ export class Mp3Demuxer extends Demuxer {
|
||||
await this.advanceReader();
|
||||
}
|
||||
|
||||
// There has to be a frame if this demuxer got selected
|
||||
assert(this.firstFrameHeader);
|
||||
if (!this.firstFrameHeader) {
|
||||
throw new Error('No valid MP3 frame found.');
|
||||
}
|
||||
|
||||
this.tracks = [new InputAudioTrack(new Mp3AudioTrackBacking(this))];
|
||||
})();
|
||||
|
||||
@@ -281,13 +281,13 @@ export const parseId3V2Tag = (slice: FileSlice, header: Id3V2Header, tags: Metad
|
||||
const trackText = reader.readId3V2EncodingAndText(frameEndPos);
|
||||
const parts = trackText.split('/');
|
||||
const trackNum = Number.parseInt(parts[0]!, 10);
|
||||
const trackNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(trackNum) && trackNum > 0) {
|
||||
tags.trackNumber ??= trackNum;
|
||||
}
|
||||
if (trackNumMax && Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
tags.tracksTotal ??= trackNumMax;
|
||||
if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
||||
tags.tracksTotal ??= tracksTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
@@ -296,13 +296,13 @@ export const parseId3V2Tag = (slice: FileSlice, header: Id3V2Header, tags: Metad
|
||||
const discText = reader.readId3V2EncodingAndText(frameEndPos);
|
||||
const parts = discText.split('/');
|
||||
const discNum = Number.parseInt(parts[0]!, 10);
|
||||
const discNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const discsTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(discNum) && discNum > 0) {
|
||||
tags.discNumber ??= discNum;
|
||||
}
|
||||
if (discNumMax && Number.isInteger(discNumMax) && discNumMax > 0) {
|
||||
tags.discsTotal ??= discNumMax;
|
||||
if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) {
|
||||
tags.discsTotal ??= discsTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
|
||||
+12
-12
@@ -334,40 +334,40 @@ export class OggDemuxer extends Demuxer {
|
||||
case 'TRACKNUMBER': {
|
||||
const parts = value.split('/');
|
||||
const trackNum = Number.parseInt(parts[0]!, 10);
|
||||
const trackNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(trackNum) && trackNum > 0) {
|
||||
this.metadataTags.trackNumber ??= trackNum;
|
||||
}
|
||||
if (trackNumMax && Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
this.metadataTags.tracksTotal ??= trackNumMax;
|
||||
if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
||||
this.metadataTags.tracksTotal ??= tracksTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
case 'TRACKTOTAL': {
|
||||
const trackNumMax = Number.parseInt(value, 10);
|
||||
if (Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
this.metadataTags.tracksTotal ??= trackNumMax;
|
||||
const tracksTotal = Number.parseInt(value, 10);
|
||||
if (Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
||||
this.metadataTags.tracksTotal ??= tracksTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
case 'DISCNUMBER': {
|
||||
const parts = value.split('/');
|
||||
const discNum = Number.parseInt(parts[0]!, 10);
|
||||
const discNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const discsTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(discNum) && discNum > 0) {
|
||||
this.metadataTags.discNumber ??= discNum;
|
||||
}
|
||||
if (discNumMax && Number.isInteger(discNumMax) && discNumMax > 0) {
|
||||
this.metadataTags.discsTotal ??= discNumMax;
|
||||
if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) {
|
||||
this.metadataTags.discsTotal ??= discsTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
case 'DISCTOTAL': {
|
||||
const trackNumMax = Number.parseInt(value, 10);
|
||||
if (Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
this.metadataTags.tracksTotal ??= trackNumMax;
|
||||
const discsTotal = Number.parseInt(value, 10);
|
||||
if (Number.isInteger(discsTotal) && discsTotal > 0) {
|
||||
this.metadataTags.discsTotal ??= discsTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
|
||||
@@ -334,21 +334,21 @@ export class OggMuxer extends Muxer {
|
||||
}; break;
|
||||
|
||||
case 'trackNumber': {
|
||||
addCommentTag('DISCNUMBER', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'tracksTotal': {
|
||||
addCommentTag('DISCTOTAL', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'discNumber': {
|
||||
addCommentTag('TRACKNUMBER', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'discsTotal': {
|
||||
case 'tracksTotal': {
|
||||
addCommentTag('TRACKTOTAL', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'discNumber': {
|
||||
addCommentTag('DISCNUMBER', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'discsTotal': {
|
||||
addCommentTag('DISCTOTAL', value.toString());
|
||||
}; break;
|
||||
|
||||
case 'images': {
|
||||
for (const image of value) {
|
||||
// https://datatracker.ietf.org/doc/rfc9639/ Section 8.8
|
||||
|
||||
+10
@@ -1,3 +1,12 @@
|
||||
/*!
|
||||
* Copyright (c) 2025-present, Vanilagy and contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/** @public */
|
||||
export type MetadataTags = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
@@ -22,6 +31,7 @@ export type MetadataTags = {
|
||||
raw?: Record<string, string | Uint8Array | RichImageData | null>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export class RichImageData {
|
||||
constructor(public data: Uint8Array, public mimeType: string) {}
|
||||
}
|
||||
|
||||
@@ -230,13 +230,13 @@ export class WaveDemuxer extends Demuxer {
|
||||
case 'TRCK': {
|
||||
const parts = value.split('/');
|
||||
const trackNum = Number.parseInt(parts[0]!, 10);
|
||||
const trackNumMax = parts[1] && Number.parseInt(parts[1], 10);
|
||||
const tracksTotal = parts[1] && Number.parseInt(parts[1], 10);
|
||||
|
||||
if (Number.isInteger(trackNum) && trackNum > 0) {
|
||||
this.metadataTags.trackNumber ??= trackNum;
|
||||
}
|
||||
if (trackNumMax && Number.isInteger(trackNumMax) && trackNumMax > 0) {
|
||||
this.metadataTags.tracksTotal ??= trackNumMax;
|
||||
if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) {
|
||||
this.metadataTags.tracksTotal ??= tracksTotal;
|
||||
}
|
||||
}; break;
|
||||
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { Output } from '../../src/output.js';
|
||||
import {
|
||||
MkvOutputFormat,
|
||||
MovOutputFormat,
|
||||
Mp3OutputFormat,
|
||||
Mp4OutputFormat,
|
||||
OggOutputFormat,
|
||||
WavOutputFormat,
|
||||
} from '../../src/output-format.js';
|
||||
import { BufferTarget } from '../../src/target.js';
|
||||
import { EncodedAudioPacketSource } from '../../src/media-source.js';
|
||||
import { EncodedPacket } from '../../src/packet.js';
|
||||
import { Input } from '../../src/input.js';
|
||||
import { BufferSource, FilePathSource } from '../../src/source.js';
|
||||
import { ALL_FORMATS } from '../../src/input-format.js';
|
||||
import { MetadataTags } from '../../src/tags.js';
|
||||
import path from 'node:path';
|
||||
import { AudioCodec, buildAudioCodecString } from '../../src/codec.js';
|
||||
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
|
||||
const createDummyAudioTrack = (codec: AudioCodec, output: Output) => {
|
||||
const source = new EncodedAudioPacketSource(codec);
|
||||
output.addAudioTrack(source);
|
||||
|
||||
return {
|
||||
async addPacket() {
|
||||
// Data to make it behave like an MP3 frame
|
||||
const data = new Uint8Array(2000);
|
||||
data[0] = 255;
|
||||
data[1] = 251;
|
||||
data[2] = 224;
|
||||
data[3] = 100;
|
||||
|
||||
// OpusHead description
|
||||
const description = new Uint8Array(64);
|
||||
description[0] = 0x4f;
|
||||
description[1] = 0x70;
|
||||
description[2] = 0x75;
|
||||
description[3] = 0x73;
|
||||
description[4] = 0x48;
|
||||
description[5] = 0x65;
|
||||
description[6] = 0x61;
|
||||
description[7] = 0x64;
|
||||
|
||||
await source.add(
|
||||
new EncodedPacket(data, 'key', 0, 1),
|
||||
{
|
||||
decoderConfig: {
|
||||
codec: buildAudioCodecString(codec, 2, 48000),
|
||||
numberOfChannels: 2,
|
||||
sampleRate: 48000,
|
||||
description,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const coverArt = new Uint8Array(1024);
|
||||
coverArt[0] = 69;
|
||||
const songMetadata: MetadataTags = {
|
||||
title: 'Trying to Feel Alive',
|
||||
description: 'A song',
|
||||
artist: 'Porter Robinson & others',
|
||||
album: 'Nurture',
|
||||
albumArtist: 'Porter Robinson',
|
||||
genre: 'Electronic',
|
||||
comment: 'Some of this info is intentionally incorrect',
|
||||
lyrics: 'Well, do you feel better now?\nI thought I\'d run until the sky came out',
|
||||
trackNumber: 13,
|
||||
tracksTotal: 14,
|
||||
discNumber: 1,
|
||||
discsTotal: 1,
|
||||
date: new Date(2021, 3, 23),
|
||||
images: [{
|
||||
data: coverArt,
|
||||
kind: 'coverFront',
|
||||
mimeType: 'image/jpeg',
|
||||
description: 'This image shows a person laying in a field of grass',
|
||||
}],
|
||||
};
|
||||
|
||||
test('Read and write metadata, MP4', async () => {
|
||||
const output = new Output({
|
||||
format: new Mp4OutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
'©hog': 'pish',
|
||||
'©nam': 'Cheerleader', // Test that it doesn't override the main title
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('aac', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBe(songMetadata.description);
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBe(songMetadata.albumArtist);
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBe(songMetadata.lyrics);
|
||||
expect(readTags.trackNumber).toBe(songMetadata.trackNumber);
|
||||
expect(readTags.tracksTotal).toBe(songMetadata.tracksTotal);
|
||||
expect(readTags.discNumber).toBe(songMetadata.discNumber);
|
||||
expect(readTags.discsTotal).toBe(songMetadata.discsTotal);
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toHaveLength(1);
|
||||
expect(readTags.images![0]!.data).toEqual(coverArt);
|
||||
expect(readTags.images![0]!.mimeType).toEqual('image/jpeg');
|
||||
expect(readTags.images![0]!.kind).toEqual('coverFront');
|
||||
expect(readTags.images![0]!.description).toBeUndefined(); // Lost in MP4
|
||||
|
||||
expect(readTags.raw!['©nam']).toBe(songMetadata.title);
|
||||
expect(readTags.raw!['trkn']).instanceOf(Uint8Array);
|
||||
expect(readTags.raw!['©hog']).toBe('pish');
|
||||
});
|
||||
|
||||
test('Read and write metadata, QuickTime', async () => {
|
||||
const output = new Output({
|
||||
format: new MovOutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
'©sic': 'ko mode',
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('aac', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBe(songMetadata.description);
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBe(songMetadata.albumArtist);
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBe(songMetadata.lyrics);
|
||||
expect(readTags.trackNumber).toBeUndefined(); // All of these don't work in MOV
|
||||
expect(readTags.tracksTotal).toBeUndefined();
|
||||
expect(readTags.discNumber).toBeUndefined();
|
||||
expect(readTags.discsTotal).toBeUndefined();
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toBeUndefined();
|
||||
|
||||
expect(readTags.raw!['©nam']).toBe(songMetadata.title);
|
||||
// We don't know what the data type is, so the demuxer just returns Uint8Array
|
||||
expect(readTags.raw!['©sic']).toEqual(new Uint8Array([
|
||||
0, 7, // String length 7
|
||||
85, 196, // Language code for 'und'
|
||||
107, 111, 32, 109, 111, 100, 101, // 'ko mode'
|
||||
]));
|
||||
});
|
||||
|
||||
test('Read MOV metadata tags, ilst with keys', async () => {
|
||||
const input = new Input({
|
||||
source: new FilePathSource(path.join(__dirname, '../public/trunc-buck-bunny.mov')),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const tags = await input.getMetadataTags();
|
||||
expect(tags.raw).not.toBeUndefined();
|
||||
expect(tags.raw!['com.apple.quicktime.version']).toBe('7.4.1 (14) 0x7418000 (Mac OS X, 10.5.2, 9C31)');
|
||||
expect(tags.raw!['com.apple.quicktime.player.movie.audio.gain']).toEqual(new Uint8Array([63, 128, 0, 0]));
|
||||
});
|
||||
|
||||
test('Read and write metadata, Matroska', async () => {
|
||||
const output = new Output({
|
||||
format: new MkvOutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
CUSTOM: 'Levels',
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('opus', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBe(songMetadata.description);
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBe(songMetadata.albumArtist);
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBe(songMetadata.lyrics);
|
||||
expect(readTags.trackNumber).toBe(songMetadata.trackNumber);
|
||||
expect(readTags.tracksTotal).toBe(songMetadata.tracksTotal);
|
||||
expect(readTags.discNumber).toBe(songMetadata.discNumber);
|
||||
expect(readTags.discsTotal).toBe(songMetadata.discsTotal);
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toHaveLength(1);
|
||||
expect(readTags.images![0]!.data).toEqual(coverArt);
|
||||
expect(readTags.images![0]!.mimeType).toEqual('image/jpeg');
|
||||
expect(readTags.images![0]!.kind).toEqual('coverFront');
|
||||
expect(readTags.images![0]!.description).toEqual(songMetadata.images![0]!.description);
|
||||
expect(readTags.images![0]!.name).toEqual('cover.jpg'); // It constructed this name
|
||||
|
||||
expect(readTags.raw!['TITLE']).toBe(songMetadata.title);
|
||||
expect(readTags.raw!['PART_NUMBER']).toBe('13/14');
|
||||
expect(readTags.raw!['CUSTOM']).toBe('Levels');
|
||||
});
|
||||
|
||||
test('Read and write metadata, MP3', async () => {
|
||||
const output = new Output({
|
||||
format: new Mp3OutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
TXXY: 'ID3v2 goated',
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('mp3', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
// ID3v2 is goated, so pretty much everything was copied:
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBe(songMetadata.description);
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBe(songMetadata.albumArtist);
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBe(songMetadata.lyrics);
|
||||
expect(readTags.trackNumber).toBe(songMetadata.trackNumber);
|
||||
expect(readTags.tracksTotal).toBe(songMetadata.tracksTotal);
|
||||
expect(readTags.discNumber).toBe(songMetadata.discNumber);
|
||||
expect(readTags.discsTotal).toBe(songMetadata.discsTotal);
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toHaveLength(1);
|
||||
expect(readTags.images![0]!.data).toEqual(coverArt);
|
||||
expect(readTags.images![0]!.mimeType).toEqual('image/jpeg');
|
||||
expect(readTags.images![0]!.kind).toEqual('coverFront');
|
||||
expect(readTags.images![0]!.description).toEqual(songMetadata.images![0]!.description);
|
||||
expect(readTags.images![0]!.name).toBeUndefined(); // Can't be contained in ID3v2
|
||||
|
||||
expect(readTags.raw!['TIT2']).toBe(songMetadata.title);
|
||||
expect(readTags.raw!['APIC']).instanceOf(Uint8Array);
|
||||
expect(readTags.raw!['TXXY']).toBe('ID3v2 goated');
|
||||
});
|
||||
|
||||
test('Read and write metadata, Ogg', async () => {
|
||||
const output = new Output({
|
||||
format: new OggOutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
vendor: 'mediabunny corp',
|
||||
COMPOSER: 'Hans Zimmer',
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('opus', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBe(songMetadata.description);
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBe(songMetadata.albumArtist);
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBe(songMetadata.lyrics);
|
||||
expect(readTags.trackNumber).toBe(songMetadata.trackNumber);
|
||||
expect(readTags.tracksTotal).toBe(songMetadata.tracksTotal);
|
||||
expect(readTags.discNumber).toBe(songMetadata.discNumber);
|
||||
expect(readTags.discsTotal).toBe(songMetadata.discsTotal);
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toHaveLength(1);
|
||||
expect(readTags.images![0]!.data).toEqual(coverArt);
|
||||
expect(readTags.images![0]!.mimeType).toEqual('image/jpeg');
|
||||
expect(readTags.images![0]!.kind).toEqual('coverFront');
|
||||
expect(readTags.images![0]!.description).toEqual(songMetadata.images![0]!.description);
|
||||
expect(readTags.images![0]!.name).toBeUndefined(); // Can't be contained in Vorbis-style metadata
|
||||
|
||||
expect(readTags.raw!['vendor']).toBe('mediabunny corp');
|
||||
expect(readTags.raw!['COMPOSER']).toBe('Hans Zimmer');
|
||||
});
|
||||
|
||||
test('Read and write metadata, WAVE', async () => {
|
||||
const output = new Output({
|
||||
format: new WavOutputFormat(),
|
||||
target: new BufferTarget(),
|
||||
});
|
||||
|
||||
output.setMetadataTags({
|
||||
...songMetadata,
|
||||
raw: {
|
||||
IKEK: 'RIFF INFO lowkey mid',
|
||||
},
|
||||
});
|
||||
|
||||
const dummyTrack = createDummyAudioTrack('pcm-s16', output);
|
||||
|
||||
await output.start();
|
||||
await dummyTrack.addPacket();
|
||||
await output.finalize();
|
||||
|
||||
const input = new Input({
|
||||
source: new BufferSource(output.target.buffer!),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const readTags = await input.getMetadataTags();
|
||||
|
||||
expect(readTags.title).toBe(songMetadata.title);
|
||||
expect(readTags.description).toBeUndefined();
|
||||
expect(readTags.artist).toBe(songMetadata.artist);
|
||||
expect(readTags.album).toBe(songMetadata.album);
|
||||
expect(readTags.albumArtist).toBeUndefined();
|
||||
expect(readTags.comment).toBe(songMetadata.comment);
|
||||
expect(readTags.lyrics).toBeUndefined();
|
||||
expect(readTags.trackNumber).toBe(songMetadata.trackNumber);
|
||||
expect(readTags.tracksTotal).toBe(songMetadata.tracksTotal);
|
||||
expect(readTags.discNumber).toBeUndefined();
|
||||
expect(readTags.discsTotal).toBeUndefined();
|
||||
expect(readTags.date).toEqual(readTags.date);
|
||||
expect(readTags.images).toBeUndefined();
|
||||
|
||||
expect(readTags.raw!['INAM']).toBe(songMetadata.title);
|
||||
expect(readTags.raw!['IKEK']).toBe('RIFF INFO lowkey mid');
|
||||
});
|
||||
Binary file not shown.
Reference in New Issue
Block a user