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.
Files changed (68) hide show
  1. package/CONTRIBUTING.md +293 -0
  2. package/NOTICE +34 -0
  3. package/README.md +122 -511
  4. package/dist/index.d.ts +132 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +137 -0
  7. package/dist/index.ts +220 -0
  8. package/dist/src/constants.d.ts +201 -0
  9. package/dist/src/constants.d.ts.map +1 -0
  10. package/dist/src/constants.ts +227 -0
  11. package/dist/src/errors.d.ts +89 -0
  12. package/dist/src/errors.d.ts.map +1 -0
  13. package/dist/src/errors.ts +237 -0
  14. package/dist/src/file-utils.d.ts +205 -0
  15. package/dist/src/file-utils.d.ts.map +1 -0
  16. package/dist/src/file-utils.ts +467 -0
  17. package/dist/src/file.js +47 -0
  18. package/dist/src/global.d.ts +10 -0
  19. package/dist/src/mod.d.ts +9 -0
  20. package/dist/src/mod.d.ts.map +1 -0
  21. package/dist/src/mod.ts +19 -0
  22. package/dist/src/simple.d.ts +347 -0
  23. package/dist/src/simple.d.ts.map +1 -0
  24. package/dist/src/simple.ts +659 -0
  25. package/dist/src/taglib.d.ts +502 -0
  26. package/dist/src/taglib.d.ts.map +1 -0
  27. package/dist/src/taglib.ts +959 -0
  28. package/dist/src/types.d.ts +323 -0
  29. package/dist/src/types.d.ts.map +1 -0
  30. package/dist/src/types.ts +538 -0
  31. package/dist/src/utils/file.d.ts +15 -0
  32. package/dist/src/utils/file.d.ts.map +1 -0
  33. package/dist/src/utils/file.ts +82 -0
  34. package/dist/src/utils/write.d.ts +15 -0
  35. package/dist/src/utils/write.d.ts.map +1 -0
  36. package/dist/src/utils/write.ts +61 -0
  37. package/dist/src/wasm-workers.d.ts +33 -0
  38. package/dist/src/wasm-workers.d.ts.map +1 -0
  39. package/dist/src/wasm-workers.ts +176 -0
  40. package/dist/src/wasm.d.ts +97 -0
  41. package/dist/src/wasm.d.ts.map +1 -0
  42. package/dist/src/wasm.ts +133 -0
  43. package/dist/src/web-utils.d.ts +180 -0
  44. package/dist/src/web-utils.d.ts.map +1 -0
  45. package/dist/src/web-utils.ts +347 -0
  46. package/dist/src/workers.d.ts +219 -0
  47. package/dist/src/workers.d.ts.map +1 -0
  48. package/dist/src/workers.ts +465 -0
  49. package/dist/src/write.js +33 -0
  50. package/dist/taglib-wrapper.d.ts +5 -0
  51. package/dist/taglib-wrapper.js +14 -0
  52. package/dist/taglib.wasm +0 -0
  53. package/index.ts +100 -7
  54. package/package.json +40 -16
  55. package/src/errors.ts +237 -0
  56. package/src/file-utils.ts +467 -0
  57. package/src/global.d.ts +10 -0
  58. package/src/simple.ts +399 -84
  59. package/src/taglib.ts +522 -28
  60. package/src/types.ts +1 -1
  61. package/src/utils/file.ts +82 -0
  62. package/src/utils/write.ts +61 -0
  63. package/src/wasm-workers.ts +13 -4
  64. package/src/wasm.ts +1 -1
  65. package/src/web-utils.ts +347 -0
  66. package/src/workers.ts +32 -13
  67. package/build/taglib.js +0 -2407
  68. package/build/taglib.wasm +0 -0
@@ -0,0 +1,467 @@
1
+ /**
2
+ * @fileoverview File I/O utilities for working with cover art in taglib-wasm
3
+ *
4
+ * This module provides file system helpers for saving and loading cover art
5
+ * in Node.js, Deno, and Bun environments.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { exportCoverArt, importCoverArt } from "taglib-wasm/file-utils";
10
+ *
11
+ * // Export cover art from audio file to image file
12
+ * await exportCoverArt("song.mp3", "cover.jpg");
13
+ *
14
+ * // Import cover art from image file to audio file
15
+ * await importCoverArt("song.mp3", "new-cover.png");
16
+ * ```
17
+ */
18
+
19
+ import type { Picture } from "./types.ts";
20
+ import { PictureType } from "./types.ts";
21
+ import {
22
+ readPictures,
23
+ applyPictures,
24
+ setCoverArt,
25
+ getCoverArt,
26
+ replacePictureByType,
27
+ } from "./simple.ts";
28
+ import { readFileData } from "./utils/file.ts";
29
+ import { writeFileData } from "./utils/write.ts";
30
+
31
+ /**
32
+ * Export cover art from an audio file to an image file
33
+ *
34
+ * Extracts the primary cover art (front cover if available, otherwise first picture)
35
+ * and saves it to the specified path.
36
+ *
37
+ * @param audioPath - Path to the audio file
38
+ * @param imagePath - Path where the image should be saved
39
+ * @returns Promise that resolves when the image is saved
40
+ * @throws Error if no cover art is found
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // Export cover art as JPEG
45
+ * await exportCoverArt("album/track01.mp3", "album/cover.jpg");
46
+ * ```
47
+ */
48
+ export async function exportCoverArt(
49
+ audioPath: string,
50
+ imagePath: string,
51
+ ): Promise<void> {
52
+ const coverData = await getCoverArt(audioPath);
53
+ if (!coverData) {
54
+ throw new Error(`No cover art found in: ${audioPath}`);
55
+ }
56
+
57
+ await writeFileData(imagePath, coverData);
58
+ }
59
+
60
+ /**
61
+ * Export a specific picture type from an audio file
62
+ *
63
+ * @param audioPath - Path to the audio file
64
+ * @param imagePath - Path where the image should be saved
65
+ * @param type - Picture type to export
66
+ * @returns Promise that resolves when the image is saved
67
+ * @throws Error if no picture of the specified type is found
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Export back cover
72
+ * await exportPictureByType(
73
+ * "album/track01.mp3",
74
+ * "album/back-cover.jpg",
75
+ * PictureType.BackCover
76
+ * );
77
+ * ```
78
+ */
79
+ export async function exportPictureByType(
80
+ audioPath: string,
81
+ imagePath: string,
82
+ type: PictureType,
83
+ ): Promise<void> {
84
+ const pictures = await readPictures(audioPath);
85
+ const picture = pictures.find(pic => pic.type === type);
86
+
87
+ if (!picture) {
88
+ throw new Error(`No picture of type ${type} found in: ${audioPath}`);
89
+ }
90
+
91
+ await writeFileData(imagePath, picture.data);
92
+ }
93
+
94
+ /**
95
+ * Export all pictures from an audio file
96
+ *
97
+ * Saves each picture with a numbered suffix based on its type and index.
98
+ *
99
+ * @param audioPath - Path to the audio file
100
+ * @param outputDir - Directory where images should be saved
101
+ * @param options - Export options
102
+ * @returns Promise resolving to array of created file paths
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * // Export all pictures to a directory
107
+ * const files = await exportAllPictures("song.mp3", "./artwork/");
108
+ * console.log(`Exported ${files.length} pictures`);
109
+ * ```
110
+ */
111
+ export async function exportAllPictures(
112
+ audioPath: string,
113
+ outputDir: string,
114
+ options: {
115
+ nameFormat?: (picture: Picture, index: number) => string;
116
+ } = {},
117
+ ): Promise<string[]> {
118
+ const pictures = await readPictures(audioPath);
119
+ const exportedPaths: string[] = [];
120
+
121
+ // Ensure directory ends with separator
122
+ const dir = outputDir.endsWith('/') ? outputDir : outputDir + '/';
123
+
124
+ for (let i = 0; i < pictures.length; i++) {
125
+ const picture = pictures[i];
126
+
127
+ // Determine filename
128
+ let filename: string;
129
+ if (options.nameFormat) {
130
+ filename = options.nameFormat(picture, i);
131
+ } else {
132
+ // Default naming: type-index.ext
133
+ const typeNames: Record<number, string> = {
134
+ [PictureType.FrontCover]: 'front-cover',
135
+ [PictureType.BackCover]: 'back-cover',
136
+ [PictureType.LeafletPage]: 'leaflet',
137
+ [PictureType.Media]: 'media',
138
+ [PictureType.LeadArtist]: 'lead-artist',
139
+ [PictureType.Artist]: 'artist',
140
+ [PictureType.Conductor]: 'conductor',
141
+ [PictureType.Band]: 'band',
142
+ [PictureType.Composer]: 'composer',
143
+ [PictureType.Lyricist]: 'lyricist',
144
+ [PictureType.RecordingLocation]: 'recording-location',
145
+ [PictureType.DuringRecording]: 'during-recording',
146
+ [PictureType.DuringPerformance]: 'during-performance',
147
+ [PictureType.MovieScreenCapture]: 'screen-capture',
148
+ [PictureType.ColouredFish]: 'fish',
149
+ [PictureType.Illustration]: 'illustration',
150
+ [PictureType.BandLogo]: 'band-logo',
151
+ [PictureType.PublisherLogo]: 'publisher-logo',
152
+ };
153
+
154
+ const typeName = typeNames[picture.type] || 'other';
155
+ const ext = picture.mimeType.split('/')[1] || 'jpg';
156
+ filename = `${typeName}-${i + 1}.${ext}`;
157
+ }
158
+
159
+ const fullPath = dir + filename;
160
+ await writeFileData(fullPath, picture.data);
161
+ exportedPaths.push(fullPath);
162
+ }
163
+
164
+ return exportedPaths;
165
+ }
166
+
167
+ /**
168
+ * Import cover art from an image file to an audio file
169
+ *
170
+ * Replaces all existing pictures with a single front cover from the image file.
171
+ * The audio file is modified in place.
172
+ *
173
+ * @param audioPath - Path to the audio file to update
174
+ * @param imagePath - Path to the image file to import
175
+ * @param options - Import options
176
+ * @returns Promise that resolves when the audio file is updated
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * // Replace cover art with a new image
181
+ * await importCoverArt("song.mp3", "new-cover.jpg");
182
+ * ```
183
+ */
184
+ export async function importCoverArt(
185
+ audioPath: string,
186
+ imagePath: string,
187
+ options: {
188
+ mimeType?: string;
189
+ description?: string;
190
+ } = {},
191
+ ): Promise<void> {
192
+ const imageData = await readFileData(imagePath);
193
+
194
+ // Detect MIME type from file extension if not provided
195
+ let mimeType = options.mimeType;
196
+ if (!mimeType) {
197
+ const ext = imagePath.split('.').pop()?.toLowerCase();
198
+ const mimeTypes: Record<string, string> = {
199
+ 'jpg': 'image/jpeg',
200
+ 'jpeg': 'image/jpeg',
201
+ 'png': 'image/png',
202
+ 'gif': 'image/gif',
203
+ 'webp': 'image/webp',
204
+ 'bmp': 'image/bmp',
205
+ };
206
+ mimeType = mimeTypes[ext || ''] || 'image/jpeg';
207
+ }
208
+
209
+ const modifiedBuffer = await setCoverArt(audioPath, imageData, mimeType);
210
+ await writeFileData(audioPath, modifiedBuffer);
211
+ }
212
+
213
+ /**
214
+ * Import a picture from file with specific type
215
+ *
216
+ * Adds or replaces a picture of the specified type in the audio file.
217
+ * The audio file is modified in place.
218
+ *
219
+ * @param audioPath - Path to the audio file to update
220
+ * @param imagePath - Path to the image file to import
221
+ * @param type - Picture type to set
222
+ * @param options - Import options
223
+ * @returns Promise that resolves when the audio file is updated
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * // Add a back cover
228
+ * await importPictureWithType(
229
+ * "song.mp3",
230
+ * "back-cover.jpg",
231
+ * PictureType.BackCover,
232
+ * { description: "Album back cover" }
233
+ * );
234
+ * ```
235
+ */
236
+ export async function importPictureWithType(
237
+ audioPath: string,
238
+ imagePath: string,
239
+ type: PictureType,
240
+ options: {
241
+ mimeType?: string;
242
+ description?: string;
243
+ } = {},
244
+ ): Promise<void> {
245
+ const imageData = await readFileData(imagePath);
246
+
247
+ // Detect MIME type from file extension if not provided
248
+ let mimeType = options.mimeType;
249
+ if (!mimeType) {
250
+ const ext = imagePath.split('.').pop()?.toLowerCase();
251
+ const mimeTypes: Record<string, string> = {
252
+ 'jpg': 'image/jpeg',
253
+ 'jpeg': 'image/jpeg',
254
+ 'png': 'image/png',
255
+ 'gif': 'image/gif',
256
+ 'webp': 'image/webp',
257
+ 'bmp': 'image/bmp',
258
+ };
259
+ mimeType = mimeTypes[ext || ''] || 'image/jpeg';
260
+ }
261
+
262
+ const picture: Picture = {
263
+ mimeType,
264
+ data: imageData,
265
+ type,
266
+ description: options.description,
267
+ };
268
+
269
+ const modifiedBuffer = await replacePictureByType(audioPath, picture);
270
+ await writeFileData(audioPath, modifiedBuffer);
271
+ }
272
+
273
+ /**
274
+ * Load a picture from an image file
275
+ *
276
+ * @param imagePath - Path to the image file
277
+ * @param type - Picture type (defaults to FrontCover)
278
+ * @param options - Picture options
279
+ * @returns Picture object ready to be applied to audio files
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const frontCover = await loadPictureFromFile("cover.jpg");
284
+ * const backCover = await loadPictureFromFile("back.png", PictureType.BackCover);
285
+ *
286
+ * const modifiedBuffer = await applyPictures("song.mp3", [frontCover, backCover]);
287
+ * ```
288
+ */
289
+ export async function loadPictureFromFile(
290
+ imagePath: string,
291
+ type: PictureType = PictureType.FrontCover,
292
+ options: {
293
+ mimeType?: string;
294
+ description?: string;
295
+ } = {},
296
+ ): Promise<Picture> {
297
+ const data = await readFileData(imagePath);
298
+
299
+ // Detect MIME type from file extension if not provided
300
+ let mimeType = options.mimeType;
301
+ if (!mimeType) {
302
+ const ext = imagePath.split('.').pop()?.toLowerCase();
303
+ const mimeTypes: Record<string, string> = {
304
+ 'jpg': 'image/jpeg',
305
+ 'jpeg': 'image/jpeg',
306
+ 'png': 'image/png',
307
+ 'gif': 'image/gif',
308
+ 'webp': 'image/webp',
309
+ 'bmp': 'image/bmp',
310
+ };
311
+ mimeType = mimeTypes[ext || ''] || 'image/jpeg';
312
+ }
313
+
314
+ return {
315
+ mimeType,
316
+ data,
317
+ type,
318
+ description: options.description || imagePath.split('/').pop(),
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Save a picture to an image file
324
+ *
325
+ * @param picture - Picture object to save
326
+ * @param imagePath - Path where the image should be saved
327
+ * @returns Promise that resolves when the image is saved
328
+ *
329
+ * @example
330
+ * ```typescript
331
+ * const pictures = await readPictures("song.mp3");
332
+ * for (const picture of pictures) {
333
+ * await savePictureToFile(picture, `export-${picture.type}.jpg`);
334
+ * }
335
+ * ```
336
+ */
337
+ export async function savePictureToFile(
338
+ picture: Picture,
339
+ imagePath: string,
340
+ ): Promise<void> {
341
+ await writeFileData(imagePath, picture.data);
342
+ }
343
+
344
+ /**
345
+ * Copy cover art from one audio file to another
346
+ *
347
+ * @param sourcePath - Path to source audio file
348
+ * @param targetPath - Path to target audio file (modified in place)
349
+ * @param options - Copy options
350
+ * @returns Promise that resolves when cover art is copied
351
+ * @throws Error if source has no cover art
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * // Copy cover art from one file to another
356
+ * await copyCoverArt("album/track01.mp3", "album/track02.mp3");
357
+ *
358
+ * // Copy all pictures
359
+ * await copyCoverArt("source.mp3", "target.mp3", { copyAll: true });
360
+ * ```
361
+ */
362
+ export async function copyCoverArt(
363
+ sourcePath: string,
364
+ targetPath: string,
365
+ options: {
366
+ copyAll?: boolean;
367
+ } = {},
368
+ ): Promise<void> {
369
+ if (options.copyAll) {
370
+ // Copy all pictures
371
+ const pictures = await readPictures(sourcePath);
372
+ if (pictures.length === 0) {
373
+ throw new Error(`No pictures found in: ${sourcePath}`);
374
+ }
375
+ const modifiedBuffer = await applyPictures(targetPath, pictures);
376
+ await writeFileData(targetPath, modifiedBuffer);
377
+ } else {
378
+ // Copy just the primary cover art
379
+ const coverData = await getCoverArt(sourcePath);
380
+ if (!coverData) {
381
+ throw new Error(`No cover art found in: ${sourcePath}`);
382
+ }
383
+
384
+ // Find the MIME type from the source
385
+ const pictures = await readPictures(sourcePath);
386
+ const coverPicture = pictures.find(p => p.type === PictureType.FrontCover) || pictures[0];
387
+
388
+ const modifiedBuffer = await setCoverArt(targetPath, coverData, coverPicture.mimeType);
389
+ await writeFileData(targetPath, modifiedBuffer);
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Check if cover art files exist for an audio file
395
+ *
396
+ * Looks for common cover art filenames in the same directory as the audio file.
397
+ *
398
+ * @param audioPath - Path to the audio file
399
+ * @returns Object with found cover art paths
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * const covers = await findCoverArtFiles("music/album/track01.mp3");
404
+ * if (covers.front) {
405
+ * await importCoverArt("music/album/track01.mp3", covers.front);
406
+ * }
407
+ * ```
408
+ */
409
+ export async function findCoverArtFiles(
410
+ audioPath: string,
411
+ ): Promise<{
412
+ front?: string;
413
+ back?: string;
414
+ folder?: string;
415
+ [key: string]: string | undefined;
416
+ }> {
417
+ const dir = audioPath.substring(0, audioPath.lastIndexOf('/') + 1);
418
+ const commonNames = [
419
+ 'cover', 'front', 'folder', 'album', 'artwork',
420
+ 'Cover', 'Front', 'Folder', 'Album', 'Artwork',
421
+ ];
422
+ const extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
423
+ const found: Record<string, string> = {};
424
+
425
+ // Check for common front cover names
426
+ for (const name of commonNames) {
427
+ for (const ext of extensions) {
428
+ const path = `${dir}${name}.${ext}`;
429
+ try {
430
+ // Try to read a single byte to check if file exists
431
+ const data = await readFileData(path);
432
+ if (data.length > 0) {
433
+ if (!found.front && ['cover', 'front', 'Cover', 'Front'].includes(name)) {
434
+ found.front = path;
435
+ } else if (!found.folder && ['folder', 'Folder'].includes(name)) {
436
+ found.folder = path;
437
+ } else {
438
+ found[name.toLowerCase()] = path;
439
+ }
440
+ break; // Found this name, skip other extensions
441
+ }
442
+ } catch {
443
+ // File doesn't exist, continue
444
+ }
445
+ }
446
+ }
447
+
448
+ // Check for back cover
449
+ const backNames = ['back', 'Back', 'back-cover', 'Back-Cover'];
450
+ for (const name of backNames) {
451
+ for (const ext of extensions) {
452
+ const path = `${dir}${name}.${ext}`;
453
+ try {
454
+ const data = await readFileData(path);
455
+ if (data.length > 0) {
456
+ found.back = path;
457
+ break;
458
+ }
459
+ } catch {
460
+ // File doesn't exist, continue
461
+ }
462
+ }
463
+ if (found.back) break;
464
+ }
465
+
466
+ return found;
467
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Global type declarations for cross-runtime compatibility
3
+ */
4
+
5
+ // Declare Deno global for TypeScript when not in Deno environment
6
+ declare global {
7
+ const Deno: any;
8
+ }
9
+
10
+ export {};