taglib-wasm 0.3.3 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +293 -0
- package/NOTICE +34 -0
- package/README.md +122 -511
- package/dist/index.d.ts +132 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.ts +220 -0
- package/dist/src/constants.d.ts +201 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.ts +227 -0
- package/dist/src/errors.d.ts +89 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.ts +237 -0
- package/dist/src/file-utils.d.ts +205 -0
- package/dist/src/file-utils.d.ts.map +1 -0
- package/dist/src/file-utils.ts +467 -0
- package/dist/src/file.js +47 -0
- package/dist/src/global.d.ts +10 -0
- package/dist/src/mod.d.ts +9 -0
- package/dist/src/mod.d.ts.map +1 -0
- package/dist/src/mod.ts +19 -0
- package/dist/src/simple.d.ts +347 -0
- package/dist/src/simple.d.ts.map +1 -0
- package/dist/src/simple.ts +659 -0
- package/dist/src/taglib.d.ts +502 -0
- package/dist/src/taglib.d.ts.map +1 -0
- package/dist/src/taglib.ts +959 -0
- package/dist/src/types.d.ts +323 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.ts +538 -0
- package/dist/src/utils/file.d.ts +15 -0
- package/dist/src/utils/file.d.ts.map +1 -0
- package/dist/src/utils/file.ts +82 -0
- package/dist/src/utils/write.d.ts +15 -0
- package/dist/src/utils/write.d.ts.map +1 -0
- package/dist/src/utils/write.ts +61 -0
- package/dist/src/wasm-workers.d.ts +33 -0
- package/dist/src/wasm-workers.d.ts.map +1 -0
- package/dist/src/wasm-workers.ts +176 -0
- package/dist/src/wasm.d.ts +97 -0
- package/dist/src/wasm.d.ts.map +1 -0
- package/dist/src/wasm.ts +133 -0
- package/dist/src/web-utils.d.ts +180 -0
- package/dist/src/web-utils.d.ts.map +1 -0
- package/dist/src/web-utils.ts +347 -0
- package/dist/src/workers.d.ts +219 -0
- package/dist/src/workers.d.ts.map +1 -0
- package/dist/src/workers.ts +465 -0
- package/dist/src/write.js +33 -0
- package/dist/taglib-wrapper.d.ts +5 -0
- package/dist/taglib-wrapper.js +14 -0
- package/dist/taglib.wasm +0 -0
- package/index.ts +100 -7
- package/package.json +40 -16
- package/src/errors.ts +237 -0
- package/src/file-utils.ts +467 -0
- package/src/global.d.ts +10 -0
- package/src/simple.ts +399 -84
- package/src/taglib.ts +522 -28
- package/src/types.ts +1 -1
- package/src/utils/file.ts +82 -0
- package/src/utils/write.ts +61 -0
- package/src/wasm-workers.ts +13 -4
- package/src/wasm.ts +1 -1
- package/src/web-utils.ts +347 -0
- package/src/workers.ts +32 -13
- package/build/taglib.js +0 -2407
- package/build/taglib.wasm +0 -0
package/src/simple.ts
CHANGED
|
@@ -25,7 +25,15 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { TagLib } from "./taglib.ts";
|
|
28
|
-
import type { AudioProperties, Tag } from "./types.ts";
|
|
28
|
+
import type { AudioProperties, Tag, Picture } from "./types.ts";
|
|
29
|
+
import { PictureType } from "./types.ts";
|
|
30
|
+
import {
|
|
31
|
+
FileOperationError,
|
|
32
|
+
InvalidFormatError,
|
|
33
|
+
MetadataError,
|
|
34
|
+
} from "./errors.ts";
|
|
35
|
+
import { readFileData } from "./utils/file.ts";
|
|
36
|
+
import { writeFileData } from "./utils/write.ts";
|
|
29
37
|
|
|
30
38
|
// Cached TagLib instance for auto-initialization
|
|
31
39
|
let cachedTagLib: TagLib | null = null;
|
|
@@ -46,61 +54,6 @@ async function getTagLib(): Promise<TagLib> {
|
|
|
46
54
|
return cachedTagLib as TagLib;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
/**
|
|
50
|
-
* Read a file's data from various sources.
|
|
51
|
-
* Supports file paths (Node.js/Deno/Bun), buffers, and File objects (browser).
|
|
52
|
-
*
|
|
53
|
-
* @internal
|
|
54
|
-
* @param file - File path, Uint8Array, ArrayBuffer, or File object
|
|
55
|
-
* @returns Promise resolving to Uint8Array of file data
|
|
56
|
-
* @throws {Error} If file type is not supported or read fails
|
|
57
|
-
*/
|
|
58
|
-
async function readFileData(
|
|
59
|
-
file: string | Uint8Array | ArrayBuffer | File,
|
|
60
|
-
): Promise<Uint8Array> {
|
|
61
|
-
// Already a Uint8Array
|
|
62
|
-
if (file instanceof Uint8Array) {
|
|
63
|
-
return file;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ArrayBuffer - convert to Uint8Array
|
|
67
|
-
if (file instanceof ArrayBuffer) {
|
|
68
|
-
return new Uint8Array(file);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// File object (browser)
|
|
72
|
-
if (typeof File !== "undefined" && file instanceof File) {
|
|
73
|
-
return new Uint8Array(await file.arrayBuffer());
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// String path - read from filesystem
|
|
77
|
-
if (typeof file === "string") {
|
|
78
|
-
// Deno
|
|
79
|
-
if (typeof Deno !== "undefined") {
|
|
80
|
-
return await Deno.readFile(file);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Node.js
|
|
84
|
-
if (
|
|
85
|
-
typeof process !== "undefined" && process.versions &&
|
|
86
|
-
process.versions.node
|
|
87
|
-
) {
|
|
88
|
-
const { readFile } = await import("fs/promises");
|
|
89
|
-
return new Uint8Array(await readFile(file));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Bun
|
|
93
|
-
if (typeof (globalThis as any).Bun !== "undefined") {
|
|
94
|
-
const bunFile = (globalThis as any).Bun.file(file);
|
|
95
|
-
return new Uint8Array(await bunFile.arrayBuffer());
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
throw new Error("File path reading not supported in this environment");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
throw new Error("Invalid file input type");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
57
|
/**
|
|
105
58
|
* Read metadata tags from an audio file
|
|
106
59
|
*
|
|
@@ -117,12 +70,12 @@ export async function readTags(
|
|
|
117
70
|
file: string | Uint8Array | ArrayBuffer | File,
|
|
118
71
|
): Promise<Tag> {
|
|
119
72
|
const taglib = await getTagLib();
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const audioFile = await taglib.openFile(audioData.buffer);
|
|
73
|
+
const audioFile = await taglib.open(file);
|
|
123
74
|
try {
|
|
124
75
|
if (!audioFile.isValid()) {
|
|
125
|
-
throw new
|
|
76
|
+
throw new InvalidFormatError(
|
|
77
|
+
"File may be corrupted or in an unsupported format"
|
|
78
|
+
);
|
|
126
79
|
}
|
|
127
80
|
|
|
128
81
|
return audioFile.tag();
|
|
@@ -132,19 +85,19 @@ export async function readTags(
|
|
|
132
85
|
}
|
|
133
86
|
|
|
134
87
|
/**
|
|
135
|
-
*
|
|
88
|
+
* Apply metadata tags to an audio file and return the modified buffer
|
|
136
89
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
90
|
+
* This function loads the file, applies the tag changes, and returns
|
|
91
|
+
* the modified file as a buffer. The original file is not modified.
|
|
139
92
|
*
|
|
140
93
|
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
141
|
-
* @param tags - Object containing tags to
|
|
94
|
+
* @param tags - Object containing tags to apply (undefined values are ignored)
|
|
142
95
|
* @param options - Write options (currently unused, for go-taglib compatibility)
|
|
143
|
-
* @returns Modified file buffer
|
|
96
|
+
* @returns Modified file buffer with new tags applied
|
|
144
97
|
*
|
|
145
98
|
* @example
|
|
146
99
|
* ```typescript
|
|
147
|
-
* const modifiedBuffer = await
|
|
100
|
+
* const modifiedBuffer = await applyTags("song.mp3", {
|
|
148
101
|
* title: "New Title",
|
|
149
102
|
* artist: "New Artist",
|
|
150
103
|
* album: "New Album",
|
|
@@ -153,18 +106,18 @@ export async function readTags(
|
|
|
153
106
|
* // Save modifiedBuffer to file or use as needed
|
|
154
107
|
* ```
|
|
155
108
|
*/
|
|
156
|
-
export async function
|
|
109
|
+
export async function applyTags(
|
|
157
110
|
file: string | Uint8Array | ArrayBuffer | File,
|
|
158
111
|
tags: Partial<Tag>,
|
|
159
112
|
options?: number,
|
|
160
113
|
): Promise<Uint8Array> {
|
|
161
114
|
const taglib = await getTagLib();
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
const audioFile = await taglib.openFile(audioData.buffer);
|
|
115
|
+
const audioFile = await taglib.open(file);
|
|
165
116
|
try {
|
|
166
117
|
if (!audioFile.isValid()) {
|
|
167
|
-
throw new
|
|
118
|
+
throw new InvalidFormatError(
|
|
119
|
+
"File may be corrupted or in an unsupported format"
|
|
120
|
+
);
|
|
168
121
|
}
|
|
169
122
|
|
|
170
123
|
// Get the tag object and write each tag if defined
|
|
@@ -179,7 +132,10 @@ export async function writeTags(
|
|
|
179
132
|
|
|
180
133
|
// Save changes to in-memory buffer
|
|
181
134
|
if (!audioFile.save()) {
|
|
182
|
-
throw new
|
|
135
|
+
throw new FileOperationError(
|
|
136
|
+
"save",
|
|
137
|
+
"Failed to save metadata changes. The file may be read-only or corrupted."
|
|
138
|
+
);
|
|
183
139
|
}
|
|
184
140
|
|
|
185
141
|
// Get the modified buffer after saving
|
|
@@ -189,6 +145,52 @@ export async function writeTags(
|
|
|
189
145
|
}
|
|
190
146
|
}
|
|
191
147
|
|
|
148
|
+
/**
|
|
149
|
+
* @deprecated Use `applyTags` instead. This alias will be removed in v1.0.0.
|
|
150
|
+
*/
|
|
151
|
+
export const writeTags = applyTags;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Update metadata tags in an audio file and save to disk
|
|
155
|
+
*
|
|
156
|
+
* This function modifies the file on disk by applying the specified tags
|
|
157
|
+
* and writing the changes back to the original file path.
|
|
158
|
+
*
|
|
159
|
+
* @param file - File path as a string (required for disk operations)
|
|
160
|
+
* @param tags - Object containing tags to write (undefined values are ignored)
|
|
161
|
+
* @param options - Write options (currently unused, for go-taglib compatibility)
|
|
162
|
+
* @throws {InvalidInputError} If file is not a string
|
|
163
|
+
* @throws {FileOperationError} If file write fails
|
|
164
|
+
* @returns Promise that resolves when the file has been updated on disk
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* // Update tags and save to disk
|
|
169
|
+
* await updateTags("song.mp3", {
|
|
170
|
+
* title: "New Title",
|
|
171
|
+
* artist: "New Artist"
|
|
172
|
+
* });
|
|
173
|
+
* // File on disk now has updated tags
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @see applyTags - For getting a modified buffer without writing to disk
|
|
177
|
+
*/
|
|
178
|
+
export async function updateTags(
|
|
179
|
+
file: string,
|
|
180
|
+
tags: Partial<Tag>,
|
|
181
|
+
options?: number,
|
|
182
|
+
): Promise<void> {
|
|
183
|
+
if (typeof file !== "string") {
|
|
184
|
+
throw new Error("updateTags requires a file path string to save changes");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Get the modified buffer
|
|
188
|
+
const modifiedBuffer = await applyTags(file, tags, options);
|
|
189
|
+
|
|
190
|
+
// Write the buffer back to the file
|
|
191
|
+
await writeFileData(file, modifiedBuffer);
|
|
192
|
+
}
|
|
193
|
+
|
|
192
194
|
/**
|
|
193
195
|
* Read audio properties from a file
|
|
194
196
|
*
|
|
@@ -207,17 +209,21 @@ export async function readProperties(
|
|
|
207
209
|
file: string | Uint8Array | ArrayBuffer | File,
|
|
208
210
|
): Promise<AudioProperties> {
|
|
209
211
|
const taglib = await getTagLib();
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
const audioFile = await taglib.openFile(audioData.buffer);
|
|
212
|
+
const audioFile = await taglib.open(file);
|
|
213
213
|
try {
|
|
214
214
|
if (!audioFile.isValid()) {
|
|
215
|
-
throw new
|
|
215
|
+
throw new InvalidFormatError(
|
|
216
|
+
"File may be corrupted or in an unsupported format"
|
|
217
|
+
);
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
const props = audioFile.audioProperties();
|
|
219
221
|
if (!props) {
|
|
220
|
-
throw new
|
|
222
|
+
throw new MetadataError(
|
|
223
|
+
"read",
|
|
224
|
+
"File may not contain valid audio data",
|
|
225
|
+
"audioProperties"
|
|
226
|
+
);
|
|
221
227
|
}
|
|
222
228
|
return props;
|
|
223
229
|
} finally {
|
|
@@ -269,9 +275,7 @@ export async function isValidAudioFile(
|
|
|
269
275
|
): Promise<boolean> {
|
|
270
276
|
try {
|
|
271
277
|
const taglib = await getTagLib();
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
const audioFile = await taglib.openFile(audioData.buffer);
|
|
278
|
+
const audioFile = await taglib.open(file);
|
|
275
279
|
const valid = audioFile.isValid();
|
|
276
280
|
audioFile.dispose();
|
|
277
281
|
|
|
@@ -297,9 +301,7 @@ export async function getFormat(
|
|
|
297
301
|
file: string | Uint8Array | ArrayBuffer | File,
|
|
298
302
|
): Promise<string | undefined> {
|
|
299
303
|
const taglib = await getTagLib();
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
const audioFile = await taglib.openFile(audioData.buffer);
|
|
304
|
+
const audioFile = await taglib.open(file);
|
|
303
305
|
try {
|
|
304
306
|
if (!audioFile.isValid()) {
|
|
305
307
|
return undefined;
|
|
@@ -337,8 +339,321 @@ export async function clearTags(
|
|
|
337
339
|
});
|
|
338
340
|
}
|
|
339
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Read cover art/pictures from an audio file
|
|
344
|
+
*
|
|
345
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
346
|
+
* @returns Array of Picture objects containing cover art
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* const pictures = await readPictures("song.mp3");
|
|
351
|
+
* for (const pic of pictures) {
|
|
352
|
+
* console.log(`Type: ${pic.type}, MIME: ${pic.mimeType}, Size: ${pic.data.length}`);
|
|
353
|
+
* }
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
export async function readPictures(
|
|
357
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
358
|
+
): Promise<Picture[]> {
|
|
359
|
+
const taglib = await getTagLib();
|
|
360
|
+
const audioFile = await taglib.open(file);
|
|
361
|
+
try {
|
|
362
|
+
if (!audioFile.isValid()) {
|
|
363
|
+
throw new InvalidFormatError(
|
|
364
|
+
"File may be corrupted or in an unsupported format"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return audioFile.getPictures();
|
|
369
|
+
} finally {
|
|
370
|
+
audioFile.dispose();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Apply pictures/cover art to an audio file and return the modified buffer
|
|
376
|
+
*
|
|
377
|
+
* This function loads the file, replaces all existing pictures with the new ones,
|
|
378
|
+
* and returns the modified file as a buffer. The original file is not modified.
|
|
379
|
+
*
|
|
380
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
381
|
+
* @param pictures - Array of Picture objects to set (replaces all existing)
|
|
382
|
+
* @returns Modified file buffer with new pictures applied
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* const coverArt = {
|
|
387
|
+
* mimeType: "image/jpeg",
|
|
388
|
+
* data: jpegData, // Uint8Array
|
|
389
|
+
* type: PictureType.FrontCover,
|
|
390
|
+
* description: "Album cover"
|
|
391
|
+
* };
|
|
392
|
+
* const modifiedBuffer = await applyPictures("song.mp3", [coverArt]);
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
export async function applyPictures(
|
|
396
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
397
|
+
pictures: Picture[],
|
|
398
|
+
): Promise<Uint8Array> {
|
|
399
|
+
const taglib = await getTagLib();
|
|
400
|
+
const audioFile = await taglib.open(file);
|
|
401
|
+
try {
|
|
402
|
+
if (!audioFile.isValid()) {
|
|
403
|
+
throw new InvalidFormatError(
|
|
404
|
+
"File may be corrupted or in an unsupported format"
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Set the pictures
|
|
409
|
+
audioFile.setPictures(pictures);
|
|
410
|
+
|
|
411
|
+
// Save changes to in-memory buffer
|
|
412
|
+
if (!audioFile.save()) {
|
|
413
|
+
throw new FileOperationError(
|
|
414
|
+
"save",
|
|
415
|
+
"Failed to save picture changes. The file may be read-only or corrupted."
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Get the modified buffer after saving
|
|
420
|
+
return audioFile.getFileBuffer();
|
|
421
|
+
} finally {
|
|
422
|
+
audioFile.dispose();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Add a single picture to an audio file and return the modified buffer
|
|
428
|
+
*
|
|
429
|
+
* This function loads the file, adds the picture to existing ones,
|
|
430
|
+
* and returns the modified file as a buffer. The original file is not modified.
|
|
431
|
+
*
|
|
432
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
433
|
+
* @param picture - Picture object to add
|
|
434
|
+
* @returns Modified file buffer with picture added
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* ```typescript
|
|
438
|
+
* const backCover = {
|
|
439
|
+
* mimeType: "image/png",
|
|
440
|
+
* data: pngData, // Uint8Array
|
|
441
|
+
* type: PictureType.BackCover,
|
|
442
|
+
* description: "Back cover"
|
|
443
|
+
* };
|
|
444
|
+
* const modifiedBuffer = await addPicture("song.mp3", backCover);
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
export async function addPicture(
|
|
448
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
449
|
+
picture: Picture,
|
|
450
|
+
): Promise<Uint8Array> {
|
|
451
|
+
const taglib = await getTagLib();
|
|
452
|
+
const audioFile = await taglib.open(file);
|
|
453
|
+
try {
|
|
454
|
+
if (!audioFile.isValid()) {
|
|
455
|
+
throw new InvalidFormatError(
|
|
456
|
+
"File may be corrupted or in an unsupported format"
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Add the picture
|
|
461
|
+
audioFile.addPicture(picture);
|
|
462
|
+
|
|
463
|
+
// Save changes to in-memory buffer
|
|
464
|
+
if (!audioFile.save()) {
|
|
465
|
+
throw new FileOperationError(
|
|
466
|
+
"save",
|
|
467
|
+
"Failed to save picture changes. The file may be read-only or corrupted."
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Get the modified buffer after saving
|
|
472
|
+
return audioFile.getFileBuffer();
|
|
473
|
+
} finally {
|
|
474
|
+
audioFile.dispose();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Clear all pictures from a file
|
|
480
|
+
*
|
|
481
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
482
|
+
* @returns Modified file buffer with pictures removed
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* const cleanBuffer = await clearPictures("song.mp3");
|
|
487
|
+
* // Save cleanBuffer to remove all cover art
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
export async function clearPictures(
|
|
491
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
492
|
+
): Promise<Uint8Array> {
|
|
493
|
+
return applyPictures(file, []);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Get the primary cover art from an audio file
|
|
498
|
+
*
|
|
499
|
+
* Returns the front cover if available, otherwise the first picture found.
|
|
500
|
+
* Returns null if no pictures are present.
|
|
501
|
+
*
|
|
502
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
503
|
+
* @returns Primary cover art data or null
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* ```typescript
|
|
507
|
+
* const coverArt = await getCoverArt("song.mp3");
|
|
508
|
+
* if (coverArt) {
|
|
509
|
+
* console.log(`Cover art size: ${coverArt.length} bytes`);
|
|
510
|
+
* }
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
export async function getCoverArt(
|
|
514
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
515
|
+
): Promise<Uint8Array | null> {
|
|
516
|
+
const pictures = await readPictures(file);
|
|
517
|
+
if (pictures.length === 0) {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Try to find front cover first
|
|
522
|
+
const frontCover = pictures.find(pic => pic.type === PictureType.FrontCover);
|
|
523
|
+
if (frontCover) {
|
|
524
|
+
return frontCover.data;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Return first picture if no front cover
|
|
528
|
+
return pictures[0].data;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Set the primary cover art for an audio file
|
|
533
|
+
*
|
|
534
|
+
* Replaces all existing pictures with a single front cover image.
|
|
535
|
+
*
|
|
536
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
537
|
+
* @param imageData - Image data as Uint8Array
|
|
538
|
+
* @param mimeType - MIME type of the image (e.g., "image/jpeg", "image/png")
|
|
539
|
+
* @returns Modified file buffer with cover art set
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```typescript
|
|
543
|
+
* const jpegData = await Deno.readFile("cover.jpg");
|
|
544
|
+
* const modifiedBuffer = await setCoverArt("song.mp3", jpegData, "image/jpeg");
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
export async function setCoverArt(
|
|
548
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
549
|
+
imageData: Uint8Array,
|
|
550
|
+
mimeType: string,
|
|
551
|
+
): Promise<Uint8Array> {
|
|
552
|
+
const picture: Picture = {
|
|
553
|
+
mimeType,
|
|
554
|
+
data: imageData,
|
|
555
|
+
type: PictureType.FrontCover,
|
|
556
|
+
description: "Front Cover",
|
|
557
|
+
};
|
|
558
|
+
return applyPictures(file, [picture]);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Find a picture by its type
|
|
563
|
+
*
|
|
564
|
+
* @param pictures - Array of pictures to search
|
|
565
|
+
* @param type - Picture type to find
|
|
566
|
+
* @returns Picture matching the type or null
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```typescript
|
|
570
|
+
* const pictures = await readPictures("song.mp3");
|
|
571
|
+
* const backCover = findPictureByType(pictures, PictureType.BackCover);
|
|
572
|
+
* if (backCover) {
|
|
573
|
+
* console.log("Found back cover art");
|
|
574
|
+
* }
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
export function findPictureByType(
|
|
578
|
+
pictures: Picture[],
|
|
579
|
+
type: PictureType,
|
|
580
|
+
): Picture | null {
|
|
581
|
+
return pictures.find(pic => pic.type === type) || null;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Replace or add a picture of a specific type
|
|
586
|
+
*
|
|
587
|
+
* If a picture of the given type already exists, it will be replaced.
|
|
588
|
+
* Otherwise, the new picture will be added to the existing ones.
|
|
589
|
+
*
|
|
590
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
591
|
+
* @param newPicture - Picture to add or replace
|
|
592
|
+
* @returns Modified file buffer with picture updated
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```typescript
|
|
596
|
+
* const backCover: Picture = {
|
|
597
|
+
* mimeType: "image/png",
|
|
598
|
+
* data: pngData,
|
|
599
|
+
* type: PictureType.BackCover,
|
|
600
|
+
* description: "Back cover"
|
|
601
|
+
* };
|
|
602
|
+
* const modifiedBuffer = await replacePictureByType("song.mp3", backCover);
|
|
603
|
+
* ```
|
|
604
|
+
*/
|
|
605
|
+
export async function replacePictureByType(
|
|
606
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
607
|
+
newPicture: Picture,
|
|
608
|
+
): Promise<Uint8Array> {
|
|
609
|
+
const pictures = await readPictures(file);
|
|
610
|
+
|
|
611
|
+
// Remove any existing picture of the same type
|
|
612
|
+
const filteredPictures = pictures.filter(pic => pic.type !== newPicture.type);
|
|
613
|
+
|
|
614
|
+
// Add the new picture
|
|
615
|
+
filteredPictures.push(newPicture);
|
|
616
|
+
|
|
617
|
+
return applyPictures(file, filteredPictures);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Get picture metadata without the actual image data
|
|
622
|
+
*
|
|
623
|
+
* Useful for checking what pictures are present without loading
|
|
624
|
+
* potentially large image data into memory.
|
|
625
|
+
*
|
|
626
|
+
* @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
|
|
627
|
+
* @returns Array of picture metadata (type, mimeType, description, size)
|
|
628
|
+
*
|
|
629
|
+
* @example
|
|
630
|
+
* ```typescript
|
|
631
|
+
* const metadata = await getPictureMetadata("song.mp3");
|
|
632
|
+
* for (const info of metadata) {
|
|
633
|
+
* console.log(`${info.description}: ${info.mimeType}, ${info.size} bytes`);
|
|
634
|
+
* }
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
export async function getPictureMetadata(
|
|
638
|
+
file: string | Uint8Array | ArrayBuffer | File,
|
|
639
|
+
): Promise<Array<{
|
|
640
|
+
type: PictureType;
|
|
641
|
+
mimeType: string;
|
|
642
|
+
description?: string;
|
|
643
|
+
size: number;
|
|
644
|
+
}>> {
|
|
645
|
+
const pictures = await readPictures(file);
|
|
646
|
+
return pictures.map(pic => ({
|
|
647
|
+
type: pic.type,
|
|
648
|
+
mimeType: pic.mimeType,
|
|
649
|
+
description: pic.description,
|
|
650
|
+
size: pic.data.length,
|
|
651
|
+
}));
|
|
652
|
+
}
|
|
653
|
+
|
|
340
654
|
/**
|
|
341
655
|
* Re-export commonly used types for convenience.
|
|
342
656
|
* These types define the structure of metadata and audio properties.
|
|
343
657
|
*/
|
|
344
|
-
export type { AudioProperties, Tag } from "./types.ts";
|
|
658
|
+
export type { AudioProperties, Tag, Picture } from "./types.ts";
|
|
659
|
+
export { PictureType } from "./types.ts";
|