Add metadata read/write support to ISOBMFF, add more metadata fields, add raw metadata support

This commit is contained in:
Vanilagy
2025-09-04 21:28:23 +02:00
parent 7f310eccdf
commit efa933863d
9 changed files with 967 additions and 174 deletions
+53 -8
View File
@@ -2,13 +2,16 @@
export type MediaMetadata = {
title?: string;
description?: string;
artist?: string;
album?: string;
albumArtist?: string;
trackNumber?: number;
trackNumberMax?: number;
discNumber?: number;
discNumberMax?: number;
genre?: string;
releasedAt?: Date;
date?: Date;
lyrics?: string;
images?: {
data: Uint8Array;
@@ -18,8 +21,13 @@ export type MediaMetadata = {
description?: string;
}[];
comment?: string;
raw?: Record<string, string | Uint8Array | RichImageData | null>;
};
export class RichImageData {
constructor(public data: Uint8Array, public mimeType: string) {}
}
export const validateMediaMetadata = (metadata: MediaMetadata) => {
if (!metadata || typeof metadata !== 'object') {
throw new TypeError('metadata must be an object.');
@@ -27,6 +35,9 @@ export const validateMediaMetadata = (metadata: MediaMetadata) => {
if (metadata.title !== undefined && typeof metadata.title !== 'string') {
throw new TypeError('metadata.title, when provided, must be a string.');
}
if (metadata.description !== undefined && typeof metadata.description !== 'string') {
throw new TypeError('metadata.description, when provided, must be a string.');
}
if (metadata.artist !== undefined && typeof metadata.artist !== 'string') {
throw new TypeError('metadata.artist, when provided, must be a string.');
}
@@ -39,21 +50,30 @@ export const validateMediaMetadata = (metadata: MediaMetadata) => {
if (metadata.trackNumber !== undefined && (!Number.isInteger(metadata.trackNumber) || metadata.trackNumber <= 0)) {
throw new TypeError('metadata.trackNumber, when provided, must be a positive integer.');
}
if (
metadata.trackNumberMax !== undefined
&& (!Number.isInteger(metadata.trackNumberMax) || metadata.trackNumberMax <= 0)
) {
throw new TypeError('metadata.trackNumberMax, when provided, must be a positive integer.');
}
if (metadata.discNumber !== undefined && (!Number.isInteger(metadata.discNumber) || metadata.discNumber <= 0)) {
throw new TypeError('metadata.discNumber, when provided, must be a positive integer.');
}
if (
metadata.discNumberMax !== undefined
&& (!Number.isInteger(metadata.discNumberMax) || metadata.discNumberMax <= 0)
) {
throw new TypeError('metadata.discNumberMax, when provided, must be a positive integer.');
}
if (metadata.genre !== undefined && typeof metadata.genre !== 'string') {
throw new TypeError('metadata.genre, when provided, must be a string.');
}
if (metadata.releasedAt !== undefined && !(metadata.releasedAt instanceof Date)) {
throw new TypeError('metadata.releasedAt, when provided, must be a Date.');
if (metadata.date !== undefined && !(metadata.date instanceof Date)) {
throw new TypeError('metadata.date, when provided, must be a Date.');
}
if (metadata.lyrics !== undefined && typeof metadata.lyrics !== 'string') {
throw new TypeError('metadata.lyrics, when provided, must be a string.');
}
if (metadata.comment !== undefined && typeof metadata.comment !== 'string') {
throw new TypeError('metadata.comment, when provided, must be a string.');
}
if (metadata.images !== undefined) {
if (!Array.isArray(metadata.images)) {
throw new TypeError('metadata.images, when provided, must be an array.');
@@ -73,18 +93,43 @@ export const validateMediaMetadata = (metadata: MediaMetadata) => {
}
}
}
if (metadata.comment !== undefined && typeof metadata.comment !== 'string') {
throw new TypeError('metadata.comment, when provided, must be a string.');
}
if (metadata.raw !== undefined) {
if (!metadata.raw || typeof metadata.raw !== 'object') {
throw new TypeError('metadata.raw, when provided, must be an object.');
}
for (const value of Object.values(metadata.raw)) {
if (
value !== null
&& typeof value !== 'string'
&& !(value instanceof Uint8Array)
&& !(value instanceof RichImageData)
) {
throw new TypeError(
'Each value in metadata.raw must be a string, Uint8Array, RichImageData, or null.',
);
}
}
}
};
export const mediaMetadataIsEmpty = (metadata: MediaMetadata) => {
return metadata.title === undefined
&& metadata.description === undefined
&& metadata.artist === undefined
&& metadata.album === undefined
&& metadata.albumArtist === undefined
&& metadata.trackNumber === undefined
&& metadata.trackNumberMax === undefined
&& metadata.discNumber === undefined
&& metadata.discNumberMax === undefined
&& metadata.genre === undefined
&& metadata.releasedAt === undefined
&& metadata.date === undefined
&& metadata.lyrics === undefined
&& (!metadata.images || metadata.images.length === 0)
&& metadata.comment === undefined
&& (!metadata.images || metadata.images.length === 0);
&& (metadata.raw === undefined || Object.keys(metadata.raw).length === 0);
};