taglib-wasm 0.3.13 → 0.3.15

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 (82) hide show
  1. package/README.md +22 -35
  2. package/dist/index.d.ts +10 -10
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +117 -134
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/constants.js +211 -0
  7. package/dist/src/constants.js.map +1 -0
  8. package/dist/src/errors.js +180 -0
  9. package/dist/src/errors.js.map +1 -0
  10. package/dist/src/file-utils.d.ts +2 -2
  11. package/dist/src/file-utils.d.ts.map +1 -1
  12. package/dist/src/file-utils.js +394 -0
  13. package/dist/src/file-utils.js.map +1 -0
  14. package/dist/src/mod.d.ts +4 -4
  15. package/dist/src/mod.d.ts.map +1 -1
  16. package/dist/src/mod.js +7 -0
  17. package/dist/src/mod.js.map +1 -0
  18. package/dist/src/simple.d.ts +4 -4
  19. package/dist/src/simple.d.ts.map +1 -1
  20. package/dist/src/{simple.ts → simple.js} +193 -311
  21. package/dist/src/simple.js.map +1 -0
  22. package/dist/src/taglib.d.ts +3 -3
  23. package/dist/src/taglib.d.ts.map +1 -1
  24. package/dist/src/taglib.js +516 -0
  25. package/dist/src/taglib.js.map +1 -0
  26. package/dist/src/types.d.ts +2 -2
  27. package/dist/src/types.d.ts.map +1 -1
  28. package/dist/src/types.js +239 -0
  29. package/dist/src/types.js.map +1 -0
  30. package/dist/src/utils/file.js +65 -0
  31. package/dist/src/utils/file.js.map +1 -0
  32. package/dist/src/utils/write.js +49 -0
  33. package/dist/src/utils/write.js.map +1 -0
  34. package/dist/src/wasm-workers.d.ts +1 -1
  35. package/dist/src/wasm-workers.d.ts.map +1 -1
  36. package/dist/src/wasm-workers.js +148 -0
  37. package/dist/src/wasm-workers.js.map +1 -0
  38. package/dist/src/wasm.js +6 -0
  39. package/dist/src/wasm.js.map +1 -0
  40. package/dist/src/web-utils.d.ts +2 -2
  41. package/dist/src/web-utils.d.ts.map +1 -1
  42. package/{src/web-utils.ts → dist/src/web-utils.js} +102 -184
  43. package/dist/src/web-utils.js.map +1 -0
  44. package/dist/src/workers.d.ts +2 -2
  45. package/dist/src/workers.d.ts.map +1 -1
  46. package/dist/src/workers.js +389 -0
  47. package/dist/src/workers.js.map +1 -0
  48. package/dist/taglib-wrapper.js +8 -2528
  49. package/package.json +7 -10
  50. package/dist/index.ts +0 -221
  51. package/dist/src/constants.ts +0 -227
  52. package/dist/src/errors.ts +0 -254
  53. package/dist/src/file-utils.ts +0 -483
  54. package/dist/src/file.js +0 -52
  55. package/dist/src/global.d.ts +0 -12
  56. package/dist/src/mod.ts +0 -19
  57. package/dist/src/taglib.ts +0 -961
  58. package/dist/src/types.ts +0 -538
  59. package/dist/src/utils/file.ts +0 -86
  60. package/dist/src/utils/write.ts +0 -66
  61. package/dist/src/wasm-workers.ts +0 -176
  62. package/dist/src/wasm.ts +0 -133
  63. package/dist/src/web-utils.ts +0 -347
  64. package/dist/src/workers.ts +0 -461
  65. package/dist/src/write.js +0 -33
  66. package/index.ts +0 -221
  67. package/lib/taglib/COPYING.LGPL +0 -502
  68. package/lib/taglib/COPYING.MPL +0 -470
  69. package/lib/taglib/README.md +0 -24
  70. package/src/constants.ts +0 -227
  71. package/src/errors.ts +0 -254
  72. package/src/file-utils.ts +0 -483
  73. package/src/global.d.ts +0 -12
  74. package/src/mod.ts +0 -19
  75. package/src/simple.ts +0 -667
  76. package/src/taglib.ts +0 -961
  77. package/src/types.ts +0 -538
  78. package/src/utils/file.ts +0 -86
  79. package/src/utils/write.ts +0 -66
  80. package/src/wasm-workers.ts +0 -176
  81. package/src/wasm.ts +0 -133
  82. package/src/workers.ts +0 -461
package/src/taglib.ts DELETED
@@ -1,961 +0,0 @@
1
- import type { TagLibModule, WasmModule } from "./wasm.ts";
2
- import type {
3
- AudioProperties,
4
- FileType,
5
- Picture,
6
- PropertyMap,
7
- Tag as BasicTag,
8
- } from "./types.ts";
9
- import {
10
- InvalidFormatError,
11
- MetadataError,
12
- TagLibInitializationError,
13
- UnsupportedFormatError,
14
- } from "./errors.ts";
15
- import { readFileData } from "./utils/file.ts";
16
- import { writeFileData } from "./utils/write.ts";
17
-
18
- /**
19
- * Extended Tag interface with read/write capabilities for audio metadata.
20
- * Extends the basic Tag interface with setter methods for modifying metadata.
21
- *
22
- * @example
23
- * ```typescript
24
- * const file = await taglib.open("song.mp3");
25
- * const tag = file.tag();
26
- *
27
- * // Read metadata
28
- * console.log(tag.title);
29
- *
30
- * // Write metadata
31
- * tag.setTitle("New Title");
32
- * tag.setArtist("New Artist");
33
- * file.save();
34
- * ```
35
- */
36
- export interface Tag extends BasicTag {
37
- /** Set the track title */
38
- setTitle(value: string): void;
39
- /** Set the artist name */
40
- setArtist(value: string): void;
41
- /** Set the album name */
42
- setAlbum(value: string): void;
43
- /** Set the comment */
44
- setComment(value: string): void;
45
- /** Set the genre */
46
- setGenre(value: string): void;
47
- /** Set the release year */
48
- setYear(value: number): void;
49
- /** Set the track number */
50
- setTrack(value: number): void;
51
- }
52
-
53
- /**
54
- * Represents an audio file with metadata and audio properties.
55
- * Provides methods for reading and writing metadata, accessing audio properties,
56
- * and managing format-specific features.
57
- *
58
- * @example
59
- * ```typescript
60
- * const file = await taglib.open("song.mp3");
61
- *
62
- * // Check if valid
63
- * if (!file.isValid()) {
64
- * throw new Error("Invalid audio file");
65
- * }
66
- *
67
- * // Read metadata
68
- * const tag = file.tag();
69
- * const props = file.audioProperties();
70
- *
71
- * // Modify and save
72
- * tag.setTitle("New Title");
73
- * file.save();
74
- *
75
- * // Get modified buffer
76
- * const modifiedBuffer = file.getFileBuffer();
77
- *
78
- * // Clean up
79
- * file.dispose();
80
- * ```
81
- */
82
- export interface AudioFile {
83
- /**
84
- * Get the audio file format.
85
- * @returns The detected file type (e.g., "MP3", "FLAC", "MP4")
86
- */
87
- getFormat(): FileType;
88
-
89
- /**
90
- * Get the tag object for reading/writing basic metadata.
91
- * @returns Tag object with getters and setters for metadata fields
92
- * @throws {Error} If unable to get tag from file
93
- */
94
- tag(): Tag;
95
-
96
- /**
97
- * Get audio properties (duration, bitrate, sample rate, etc.).
98
- * @returns Audio properties or null if unavailable
99
- */
100
- audioProperties(): AudioProperties | null;
101
-
102
- /**
103
- * Get all metadata properties as a key-value map.
104
- * Includes both standard and format-specific properties.
105
- * @returns PropertyMap with all available metadata
106
- */
107
- properties(): PropertyMap;
108
-
109
- /**
110
- * Set multiple properties at once from a PropertyMap.
111
- * @param properties - Map of property names to values
112
- */
113
- setProperties(properties: PropertyMap): void;
114
-
115
- /**
116
- * Get a single property value by key.
117
- * @param key - Property name (e.g., "ALBUMARTIST", "ACOUSTID_ID")
118
- * @returns Property value or undefined if not found
119
- */
120
- getProperty(key: string): string | undefined;
121
-
122
- /**
123
- * Set a single property value.
124
- * @param key - Property name
125
- * @param value - Property value
126
- */
127
- setProperty(key: string, value: string): void;
128
-
129
- /**
130
- * Check if this is an MP4/M4A file.
131
- * @returns true if the file is MP4/M4A format
132
- */
133
- isMP4(): boolean;
134
-
135
- /**
136
- * Get an MP4-specific metadata item.
137
- * @param key - MP4 atom name (e.g., "----:com.apple.iTunes:iTunNORM")
138
- * @returns Item value or undefined if not found
139
- * @throws {Error} If not an MP4 file
140
- */
141
- getMP4Item(key: string): string | undefined;
142
-
143
- /**
144
- * Set an MP4-specific metadata item.
145
- * @param key - MP4 atom name
146
- * @param value - Item value
147
- * @throws {Error} If not an MP4 file
148
- */
149
- setMP4Item(key: string, value: string): void;
150
-
151
- /**
152
- * Remove an MP4-specific metadata item.
153
- * @param key - MP4 atom name to remove
154
- * @throws {Error} If not an MP4 file
155
- */
156
- removeMP4Item(key: string): void;
157
-
158
- /**
159
- * Save all changes to the in-memory buffer.
160
- * Note: This does not write to disk, but updates the internal buffer.
161
- * Use getFileBuffer() to retrieve the modified data.
162
- * @returns true if save was successful
163
- */
164
- save(): boolean;
165
-
166
- /**
167
- * Get the current file data as a buffer, including any modifications.
168
- * Call this after save() to get the updated file data.
169
- * @returns Uint8Array containing the complete file data
170
- */
171
- getFileBuffer(): Uint8Array;
172
-
173
- /**
174
- * Save all changes to a file on disk.
175
- * This first saves changes to the in-memory buffer, then writes to the specified path.
176
- * @param path - Optional file path. If not provided, saves to the original path (if opened from a file).
177
- * @throws {Error} If no path is available or write fails
178
- */
179
- saveToFile(path?: string): Promise<void>;
180
-
181
- /**
182
- * Check if the file was loaded successfully and is valid.
183
- * @returns true if the file is valid and can be processed
184
- */
185
- isValid(): boolean;
186
-
187
- /**
188
- * Get all pictures/cover art from the audio file.
189
- * @returns Array of Picture objects
190
- */
191
- getPictures(): Picture[];
192
-
193
- /**
194
- * Set pictures/cover art in the audio file (replaces all existing).
195
- * @param pictures - Array of Picture objects to set
196
- */
197
- setPictures(pictures: Picture[]): void;
198
-
199
- /**
200
- * Add a single picture to the audio file.
201
- * @param picture - Picture object to add
202
- */
203
- addPicture(picture: Picture): void;
204
-
205
- /**
206
- * Remove all pictures from the audio file.
207
- */
208
- removePictures(): void;
209
-
210
- /**
211
- * Release all resources associated with this file.
212
- * Always call this when done to prevent memory leaks.
213
- */
214
- dispose(): void;
215
-
216
- // Extended metadata helper methods
217
-
218
- /**
219
- * Get MusicBrainz Track ID.
220
- * @returns MusicBrainz Track ID or undefined if not set
221
- */
222
- getMusicBrainzTrackId(): string | undefined;
223
-
224
- /**
225
- * Set MusicBrainz Track ID.
226
- * @param id - MusicBrainz Track ID (UUID format)
227
- */
228
- setMusicBrainzTrackId(id: string): void;
229
-
230
- /**
231
- * Get MusicBrainz Release ID.
232
- * @returns MusicBrainz Release ID or undefined if not set
233
- */
234
- getMusicBrainzReleaseId(): string | undefined;
235
-
236
- /**
237
- * Set MusicBrainz Release ID.
238
- * @param id - MusicBrainz Release ID (UUID format)
239
- */
240
- setMusicBrainzReleaseId(id: string): void;
241
-
242
- /**
243
- * Get MusicBrainz Artist ID.
244
- * @returns MusicBrainz Artist ID or undefined if not set
245
- */
246
- getMusicBrainzArtistId(): string | undefined;
247
-
248
- /**
249
- * Set MusicBrainz Artist ID.
250
- * @param id - MusicBrainz Artist ID (UUID format)
251
- */
252
- setMusicBrainzArtistId(id: string): void;
253
-
254
- /**
255
- * Get AcoustID fingerprint.
256
- * @returns AcoustID fingerprint or undefined if not set
257
- */
258
- getAcoustIdFingerprint(): string | undefined;
259
-
260
- /**
261
- * Set AcoustID fingerprint.
262
- * @param fingerprint - AcoustID fingerprint
263
- */
264
- setAcoustIdFingerprint(fingerprint: string): void;
265
-
266
- /**
267
- * Get AcoustID ID.
268
- * @returns AcoustID ID or undefined if not set
269
- */
270
- getAcoustIdId(): string | undefined;
271
-
272
- /**
273
- * Set AcoustID ID.
274
- * @param id - AcoustID ID
275
- */
276
- setAcoustIdId(id: string): void;
277
-
278
- /**
279
- * Get ReplayGain track gain.
280
- * @returns ReplayGain track gain (e.g., "-6.54 dB") or undefined
281
- */
282
- getReplayGainTrackGain(): string | undefined;
283
-
284
- /**
285
- * Set ReplayGain track gain.
286
- * @param gain - ReplayGain track gain (e.g., "-6.54 dB")
287
- */
288
- setReplayGainTrackGain(gain: string): void;
289
-
290
- /**
291
- * Get ReplayGain track peak.
292
- * @returns ReplayGain track peak (0.0-1.0) or undefined
293
- */
294
- getReplayGainTrackPeak(): string | undefined;
295
-
296
- /**
297
- * Set ReplayGain track peak.
298
- * @param peak - ReplayGain track peak (0.0-1.0)
299
- */
300
- setReplayGainTrackPeak(peak: string): void;
301
-
302
- /**
303
- * Get ReplayGain album gain.
304
- * @returns ReplayGain album gain (e.g., "-7.89 dB") or undefined
305
- */
306
- getReplayGainAlbumGain(): string | undefined;
307
-
308
- /**
309
- * Set ReplayGain album gain.
310
- * @param gain - ReplayGain album gain (e.g., "-7.89 dB")
311
- */
312
- setReplayGainAlbumGain(gain: string): void;
313
-
314
- /**
315
- * Get ReplayGain album peak.
316
- * @returns ReplayGain album peak (0.0-1.0) or undefined
317
- */
318
- getReplayGainAlbumPeak(): string | undefined;
319
-
320
- /**
321
- * Set ReplayGain album peak.
322
- * @param peak - ReplayGain album peak (0.0-1.0)
323
- */
324
- setReplayGainAlbumPeak(peak: string): void;
325
-
326
- /**
327
- * Get Apple Sound Check normalization data.
328
- * @returns Apple Sound Check data (iTunNORM) or undefined
329
- */
330
- getAppleSoundCheck(): string | undefined;
331
-
332
- /**
333
- * Set Apple Sound Check normalization data.
334
- * @param data - Apple Sound Check data (iTunNORM format)
335
- */
336
- setAppleSoundCheck(data: string): void;
337
- }
338
-
339
- /**
340
- * Implementation of AudioFile interface using Embind API.
341
- * Wraps the native TagLib C++ FileHandle object.
342
- *
343
- * @internal This class is not meant to be instantiated directly.
344
- * Use TagLib.open() to create instances.
345
- */
346
- export class AudioFileImpl implements AudioFile {
347
- private fileHandle: any;
348
- private cachedTag: Tag | null = null;
349
- private cachedAudioProperties: AudioProperties | null = null;
350
- private sourcePath?: string;
351
-
352
- constructor(
353
- private module: TagLibModule,
354
- fileHandle: any,
355
- sourcePath?: string,
356
- ) {
357
- this.fileHandle = fileHandle;
358
- this.sourcePath = sourcePath;
359
- }
360
-
361
- /** @inheritdoc */
362
- getFormat(): FileType {
363
- return this.fileHandle.getFormat() as FileType;
364
- }
365
-
366
- /** @inheritdoc */
367
- tag(): Tag {
368
- const tagWrapper = this.fileHandle.getTag();
369
- if (!tagWrapper) {
370
- throw new MetadataError(
371
- "read",
372
- "Tag may be corrupted or format not fully supported",
373
- );
374
- }
375
-
376
- return {
377
- title: tagWrapper.title(),
378
- artist: tagWrapper.artist(),
379
- album: tagWrapper.album(),
380
- comment: tagWrapper.comment(),
381
- genre: tagWrapper.genre(),
382
- year: tagWrapper.year(),
383
- track: tagWrapper.track(),
384
-
385
- setTitle: (value: string) => tagWrapper.setTitle(value),
386
- setArtist: (value: string) => tagWrapper.setArtist(value),
387
- setAlbum: (value: string) => tagWrapper.setAlbum(value),
388
- setComment: (value: string) => tagWrapper.setComment(value),
389
- setGenre: (value: string) => tagWrapper.setGenre(value),
390
- setYear: (value: number) => tagWrapper.setYear(value),
391
- setTrack: (value: number) => tagWrapper.setTrack(value),
392
- };
393
- }
394
-
395
- /** @inheritdoc */
396
- audioProperties(): AudioProperties | null {
397
- if (!this.cachedAudioProperties) {
398
- const propsWrapper = this.fileHandle.getAudioProperties();
399
- if (!propsWrapper) {
400
- return null;
401
- }
402
-
403
- this.cachedAudioProperties = {
404
- length: propsWrapper.lengthInSeconds(),
405
- bitrate: propsWrapper.bitrate(),
406
- sampleRate: propsWrapper.sampleRate(),
407
- channels: propsWrapper.channels(),
408
- };
409
- }
410
-
411
- return this.cachedAudioProperties;
412
- }
413
-
414
- /** @inheritdoc */
415
- properties(): PropertyMap {
416
- const jsObj = this.fileHandle.getProperties();
417
- const result: PropertyMap = {};
418
-
419
- // Convert from Emscripten val to plain object
420
- const keys = Object.keys(jsObj);
421
- for (const key of keys) {
422
- result[key] = jsObj[key];
423
- }
424
-
425
- return result;
426
- }
427
-
428
- /** @inheritdoc */
429
- setProperties(properties: PropertyMap): void {
430
- this.fileHandle.setProperties(properties);
431
- }
432
-
433
- /** @inheritdoc */
434
- getProperty(key: string): string | undefined {
435
- const value = this.fileHandle.getProperty(key);
436
- return value === "" ? undefined : value;
437
- }
438
-
439
- /** @inheritdoc */
440
- setProperty(key: string, value: string): void {
441
- this.fileHandle.setProperty(key, value);
442
- }
443
-
444
- /** @inheritdoc */
445
- isMP4(): boolean {
446
- return this.fileHandle.isMP4();
447
- }
448
-
449
- /** @inheritdoc */
450
- getMP4Item(key: string): string | undefined {
451
- if (!this.isMP4()) {
452
- const format = this.getFormat();
453
- throw new UnsupportedFormatError(
454
- format,
455
- ["MP4", "M4A"],
456
- );
457
- }
458
- const value = this.fileHandle.getMP4Item(key);
459
- return value === "" ? undefined : value;
460
- }
461
-
462
- /** @inheritdoc */
463
- setMP4Item(key: string, value: string): void {
464
- if (!this.isMP4()) {
465
- const format = this.getFormat();
466
- throw new UnsupportedFormatError(
467
- format,
468
- ["MP4", "M4A"],
469
- );
470
- }
471
- this.fileHandle.setMP4Item(key, value);
472
- }
473
-
474
- /** @inheritdoc */
475
- removeMP4Item(key: string): void {
476
- if (!this.isMP4()) {
477
- const format = this.getFormat();
478
- throw new UnsupportedFormatError(
479
- format,
480
- ["MP4", "M4A"],
481
- );
482
- }
483
- this.fileHandle.removeMP4Item(key);
484
- }
485
-
486
- /** @inheritdoc */
487
- save(): boolean {
488
- // Clear caches since values may have changed
489
- this.cachedTag = null;
490
- this.cachedAudioProperties = null;
491
-
492
- return this.fileHandle.save();
493
- }
494
-
495
- /** @inheritdoc */
496
- getFileBuffer(): Uint8Array {
497
- const buffer = this.fileHandle.getBuffer();
498
- if (!buffer) {
499
- return new Uint8Array(0);
500
- }
501
-
502
- // The buffer is already a Uint8Array from the C++ side
503
- return buffer;
504
- }
505
-
506
- /** @inheritdoc */
507
- async saveToFile(path?: string): Promise<void> {
508
- // Determine the target path
509
- const targetPath = path || this.sourcePath;
510
- if (!targetPath) {
511
- throw new Error(
512
- "No file path available. Either provide a path or open the file from a path.",
513
- );
514
- }
515
-
516
- // First save to in-memory buffer
517
- if (!this.save()) {
518
- throw new Error("Failed to save changes to in-memory buffer");
519
- }
520
-
521
- // Get the updated buffer and write to file
522
- const buffer = this.getFileBuffer();
523
- await writeFileData(targetPath, buffer);
524
- }
525
-
526
- /** @inheritdoc */
527
- isValid(): boolean {
528
- return this.fileHandle.isValid();
529
- }
530
-
531
- /** @inheritdoc */
532
- getPictures(): Picture[] {
533
- const picturesArray = this.fileHandle.getPictures();
534
- const pictures: Picture[] = [];
535
-
536
- // Convert from Emscripten array to TypeScript array
537
- for (let i = 0; i < picturesArray.length; i++) {
538
- const pic = picturesArray[i];
539
- pictures.push({
540
- mimeType: pic.mimeType,
541
- data: pic.data,
542
- type: pic.type,
543
- description: pic.description,
544
- });
545
- }
546
-
547
- return pictures;
548
- }
549
-
550
- /** @inheritdoc */
551
- setPictures(pictures: Picture[]): void {
552
- // Convert TypeScript array to format expected by C++
553
- const picturesArray = pictures.map((pic) => ({
554
- mimeType: pic.mimeType,
555
- data: pic.data,
556
- type: pic.type,
557
- description: pic.description || "",
558
- }));
559
-
560
- this.fileHandle.setPictures(picturesArray);
561
- }
562
-
563
- /** @inheritdoc */
564
- addPicture(picture: Picture): void {
565
- const pic = {
566
- mimeType: picture.mimeType,
567
- data: picture.data,
568
- type: picture.type,
569
- description: picture.description || "",
570
- };
571
-
572
- this.fileHandle.addPicture(pic);
573
- }
574
-
575
- /** @inheritdoc */
576
- removePictures(): void {
577
- this.fileHandle.removePictures();
578
- }
579
-
580
- /** @inheritdoc */
581
- dispose(): void {
582
- if (this.fileHandle) {
583
- // Explicitly destroy the C++ object to free memory immediately
584
- if (typeof this.fileHandle.destroy === "function") {
585
- this.fileHandle.destroy();
586
- }
587
- // Clear all references
588
- this.fileHandle = null;
589
- this.cachedTag = null;
590
- this.cachedAudioProperties = null;
591
- }
592
- }
593
-
594
- // Extended metadata implementations
595
-
596
- /** @inheritdoc */
597
- getMusicBrainzTrackId(): string | undefined {
598
- const value = this.getProperty("MUSICBRAINZ_TRACKID");
599
- return value || undefined;
600
- }
601
-
602
- /** @inheritdoc */
603
- setMusicBrainzTrackId(id: string): void {
604
- this.setProperty("MUSICBRAINZ_TRACKID", id);
605
- }
606
-
607
- /** @inheritdoc */
608
- getMusicBrainzReleaseId(): string | undefined {
609
- const value = this.getProperty("MUSICBRAINZ_ALBUMID");
610
- return value || undefined;
611
- }
612
-
613
- /** @inheritdoc */
614
- setMusicBrainzReleaseId(id: string): void {
615
- this.setProperty("MUSICBRAINZ_ALBUMID", id);
616
- }
617
-
618
- /** @inheritdoc */
619
- getMusicBrainzArtistId(): string | undefined {
620
- const value = this.getProperty("MUSICBRAINZ_ARTISTID");
621
- return value || undefined;
622
- }
623
-
624
- /** @inheritdoc */
625
- setMusicBrainzArtistId(id: string): void {
626
- this.setProperty("MUSICBRAINZ_ARTISTID", id);
627
- }
628
-
629
- /** @inheritdoc */
630
- getAcoustIdFingerprint(): string | undefined {
631
- const value = this.getProperty("ACOUSTID_FINGERPRINT");
632
- return value || undefined;
633
- }
634
-
635
- /** @inheritdoc */
636
- setAcoustIdFingerprint(fingerprint: string): void {
637
- this.setProperty("ACOUSTID_FINGERPRINT", fingerprint);
638
- }
639
-
640
- /** @inheritdoc */
641
- getAcoustIdId(): string | undefined {
642
- const value = this.getProperty("ACOUSTID_ID");
643
- return value || undefined;
644
- }
645
-
646
- /** @inheritdoc */
647
- setAcoustIdId(id: string): void {
648
- this.setProperty("ACOUSTID_ID", id);
649
- }
650
-
651
- /** @inheritdoc */
652
- getReplayGainTrackGain(): string | undefined {
653
- const value = this.getProperty("REPLAYGAIN_TRACK_GAIN");
654
- return value || undefined;
655
- }
656
-
657
- /** @inheritdoc */
658
- setReplayGainTrackGain(gain: string): void {
659
- this.setProperty("REPLAYGAIN_TRACK_GAIN", gain);
660
- }
661
-
662
- /** @inheritdoc */
663
- getReplayGainTrackPeak(): string | undefined {
664
- const value = this.getProperty("REPLAYGAIN_TRACK_PEAK");
665
- return value || undefined;
666
- }
667
-
668
- /** @inheritdoc */
669
- setReplayGainTrackPeak(peak: string): void {
670
- this.setProperty("REPLAYGAIN_TRACK_PEAK", peak);
671
- }
672
-
673
- /** @inheritdoc */
674
- getReplayGainAlbumGain(): string | undefined {
675
- const value = this.getProperty("REPLAYGAIN_ALBUM_GAIN");
676
- return value || undefined;
677
- }
678
-
679
- /** @inheritdoc */
680
- setReplayGainAlbumGain(gain: string): void {
681
- this.setProperty("REPLAYGAIN_ALBUM_GAIN", gain);
682
- }
683
-
684
- /** @inheritdoc */
685
- getReplayGainAlbumPeak(): string | undefined {
686
- const value = this.getProperty("REPLAYGAIN_ALBUM_PEAK");
687
- return value || undefined;
688
- }
689
-
690
- /** @inheritdoc */
691
- setReplayGainAlbumPeak(peak: string): void {
692
- this.setProperty("REPLAYGAIN_ALBUM_PEAK", peak);
693
- }
694
-
695
- /** @inheritdoc */
696
- getAppleSoundCheck(): string | undefined {
697
- // Apple Sound Check is stored differently in MP4 files
698
- if (this.isMP4()) {
699
- return this.getMP4Item("iTunNORM");
700
- }
701
- // For other formats, it might be in properties
702
- const value = this.getProperty("ITUNESOUNDCHECK");
703
- return value || undefined;
704
- }
705
-
706
- /** @inheritdoc */
707
- setAppleSoundCheck(data: string): void {
708
- // Apple Sound Check is stored differently in MP4 files
709
- if (this.isMP4()) {
710
- this.setMP4Item("iTunNORM", data);
711
- } else {
712
- // For other formats, store in properties
713
- this.setProperty("ITUNESOUNDCHECK", data);
714
- }
715
- }
716
- }
717
-
718
- /**
719
- * Main TagLib interface for audio metadata operations.
720
- * Provides methods to open audio files and access TagLib functionality.
721
- *
722
- * @example
723
- * ```typescript
724
- * // Option 1: Auto-initialize
725
- * const taglib = await TagLib.initialize();
726
- *
727
- * // Option 2: Manual initialization
728
- * import { loadTagLibModule } from "taglib-wasm";
729
- * const module = await loadTagLibModule();
730
- * const taglib = new TagLib(module);
731
- *
732
- * // Open and process a file
733
- * const file = await taglib.open("song.mp3");
734
- * const tag = file.tag();
735
- * console.log(`Title: ${tag.title}`);
736
- * file.dispose();
737
- * ```
738
- */
739
- export class TagLib {
740
- private module: TagLibModule;
741
-
742
- constructor(module: WasmModule) {
743
- this.module = module as TagLibModule;
744
- }
745
-
746
- /**
747
- * Initialize TagLib with default configuration.
748
- * This is the recommended way to create a TagLib instance.
749
- *
750
- * @returns Promise resolving to initialized TagLib instance
751
- *
752
- * @example
753
- * ```typescript
754
- * const taglib = await TagLib.initialize();
755
- * const file = await taglib.open("song.mp3");
756
- * ```
757
- */
758
- static async initialize(): Promise<TagLib> {
759
- // Use the loadTagLibModule function
760
- const { loadTagLibModule } = await import("../index.ts");
761
- const module = await loadTagLibModule();
762
- return new TagLib(module);
763
- }
764
-
765
- /**
766
- * Open an audio file from various sources.
767
- * Automatically detects the file format based on content.
768
- *
769
- * @param input - File path (string), ArrayBuffer, Uint8Array, or File object
770
- * @returns Promise resolving to AudioFile instance
771
- * @throws {Error} If the file format is invalid or unsupported
772
- * @throws {Error} If the module is not properly initialized
773
- *
774
- * @example
775
- * ```typescript
776
- * // From file path
777
- * const file = await taglib.open("song.mp3");
778
- *
779
- * // From ArrayBuffer
780
- * const file = await taglib.open(arrayBuffer);
781
- *
782
- * // From Uint8Array
783
- * const file = await taglib.open(uint8Array);
784
- *
785
- * // From File object (browser)
786
- * const file = await taglib.open(fileObject);
787
- *
788
- * // Remember to dispose when done
789
- * file.dispose();
790
- * ```
791
- */
792
- async open(
793
- input: string | ArrayBuffer | Uint8Array | File,
794
- ): Promise<AudioFile> {
795
- // Check if Embind is available
796
- if (!this.module.createFileHandle) {
797
- throw new TagLibInitializationError(
798
- "TagLib module not properly initialized: createFileHandle not found. " +
799
- "Make sure the module is fully loaded before calling open.",
800
- );
801
- }
802
-
803
- // Track the source path if input is a string
804
- const sourcePath = typeof input === "string" ? input : undefined;
805
-
806
- // Read file data if input is a path or File object
807
- const audioData = await readFileData(input);
808
-
809
- // Ensure we pass the correct slice of the buffer
810
- const buffer = audioData.buffer.slice(
811
- audioData.byteOffset,
812
- audioData.byteOffset + audioData.byteLength,
813
- );
814
-
815
- // Convert ArrayBuffer to Uint8Array for Embind
816
- const uint8Array = new Uint8Array(buffer);
817
-
818
- // Create a new FileHandle
819
- const fileHandle = this.module.createFileHandle();
820
-
821
- // Load the buffer - Embind should handle Uint8Array conversion
822
- const success = fileHandle.loadFromBuffer(uint8Array);
823
- if (!success) {
824
- throw new InvalidFormatError(
825
- "Failed to load audio file. File may be corrupted or in an unsupported format",
826
- buffer.byteLength,
827
- );
828
- }
829
-
830
- return new AudioFileImpl(this.module, fileHandle, sourcePath);
831
- }
832
-
833
- /**
834
- * Update tags in a file and save changes to disk in one operation.
835
- * This is a convenience method that opens, modifies, saves, and closes the file.
836
- *
837
- * @param path - File path to update
838
- * @param tags - Object containing tags to update
839
- * @throws {Error} If file operations fail
840
- *
841
- * @example
842
- * ```typescript
843
- * await taglib.updateFile("song.mp3", {
844
- * title: "New Title",
845
- * artist: "New Artist"
846
- * });
847
- * ```
848
- */
849
- async updateFile(path: string, tags: Partial<BasicTag>): Promise<void> {
850
- const file = await this.open(path);
851
- try {
852
- const tag = file.tag();
853
-
854
- // Apply tag updates
855
- if (tags.title !== undefined) tag.setTitle(tags.title);
856
- if (tags.artist !== undefined) tag.setArtist(tags.artist);
857
- if (tags.album !== undefined) tag.setAlbum(tags.album);
858
- if (tags.year !== undefined) tag.setYear(tags.year);
859
- if (tags.track !== undefined) tag.setTrack(tags.track);
860
- if (tags.genre !== undefined) tag.setGenre(tags.genre);
861
- if (tags.comment !== undefined) tag.setComment(tags.comment);
862
-
863
- // Save to file
864
- await file.saveToFile();
865
- } finally {
866
- file.dispose();
867
- }
868
- }
869
-
870
- /**
871
- * Copy a file with new tags.
872
- * Opens the source file, applies new tags, and saves to a new location.
873
- *
874
- * @param sourcePath - Source file path
875
- * @param destPath - Destination file path
876
- * @param tags - Object containing tags to apply
877
- * @throws {Error} If file operations fail
878
- *
879
- * @example
880
- * ```typescript
881
- * await taglib.copyWithTags("original.mp3", "copy.mp3", {
882
- * title: "Copy of Song",
883
- * comment: "This is a copy"
884
- * });
885
- * ```
886
- */
887
- async copyWithTags(
888
- sourcePath: string,
889
- destPath: string,
890
- tags: Partial<BasicTag>,
891
- ): Promise<void> {
892
- const file = await this.open(sourcePath);
893
- try {
894
- const tag = file.tag();
895
-
896
- // Apply tag updates
897
- if (tags.title !== undefined) tag.setTitle(tags.title);
898
- if (tags.artist !== undefined) tag.setArtist(tags.artist);
899
- if (tags.album !== undefined) tag.setAlbum(tags.album);
900
- if (tags.year !== undefined) tag.setYear(tags.year);
901
- if (tags.track !== undefined) tag.setTrack(tags.track);
902
- if (tags.genre !== undefined) tag.setGenre(tags.genre);
903
- if (tags.comment !== undefined) tag.setComment(tags.comment);
904
-
905
- // Save to new location
906
- await file.saveToFile(destPath);
907
- } finally {
908
- file.dispose();
909
- }
910
- }
911
-
912
- /**
913
- * Get the TagLib version.
914
- * @returns Version string (e.g., "2.1.0")
915
- */
916
- version(): string {
917
- return "2.1.0"; // TagLib version we're using
918
- }
919
- }
920
-
921
- /**
922
- * Create a TagLib instance from a pre-loaded Wasm module.
923
- * For advanced users who need custom module configuration.
924
- *
925
- * @param module - Pre-loaded Wasm module from loadTagLibModule()
926
- * @returns Promise resolving to TagLib instance
927
- *
928
- * @example
929
- * ```typescript
930
- * import { loadTagLibModule, createTagLib } from "taglib-wasm";
931
- *
932
- * const module = await loadTagLibModule();
933
- * const taglib = await createTagLib(module);
934
- * ```
935
- */
936
- export async function createTagLib(module: WasmModule): Promise<TagLib> {
937
- return new TagLib(module);
938
- }
939
-
940
- /**
941
- * Re-export error types for convenient error handling
942
- */
943
- export {
944
- EnvironmentError,
945
- FileOperationError,
946
- InvalidFormatError,
947
- isEnvironmentError,
948
- isFileOperationError,
949
- isInvalidFormatError,
950
- isMemoryError,
951
- isMetadataError,
952
- isTagLibError,
953
- isUnsupportedFormatError,
954
- MemoryError,
955
- MetadataError,
956
- SUPPORTED_FORMATS,
957
- TagLibError,
958
- TagLibErrorCode,
959
- TagLibInitializationError,
960
- UnsupportedFormatError,
961
- } from "./errors.ts";