taglib-wasm 0.3.1 → 0.3.3

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/src/taglib.ts CHANGED
@@ -1,40 +1,185 @@
1
1
  import type { TagLibModule, WasmModule } from "./wasm.ts";
2
- import type { AudioProperties, FileType, PropertyMap, Tag as BasicTag } from "./types.ts";
2
+ import type {
3
+ AudioProperties,
4
+ FileType,
5
+ PropertyMap,
6
+ Tag as BasicTag,
7
+ } from "./types.ts";
3
8
 
4
- // Extended Tag interface with setters
9
+ /**
10
+ * Extended Tag interface with read/write capabilities for audio metadata.
11
+ * Extends the basic Tag interface with setter methods for modifying metadata.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const file = await taglib.openFile(buffer);
16
+ * const tag = file.tag();
17
+ *
18
+ * // Read metadata
19
+ * console.log(tag.title);
20
+ *
21
+ * // Write metadata
22
+ * tag.setTitle("New Title");
23
+ * tag.setArtist("New Artist");
24
+ * file.save();
25
+ * ```
26
+ */
5
27
  export interface Tag extends BasicTag {
28
+ /** Set the track title */
6
29
  setTitle(value: string): void;
30
+ /** Set the artist name */
7
31
  setArtist(value: string): void;
32
+ /** Set the album name */
8
33
  setAlbum(value: string): void;
34
+ /** Set the comment */
9
35
  setComment(value: string): void;
36
+ /** Set the genre */
10
37
  setGenre(value: string): void;
38
+ /** Set the release year */
11
39
  setYear(value: number): void;
40
+ /** Set the track number */
12
41
  setTrack(value: number): void;
13
42
  }
14
43
 
15
- // Re-export types for JSR
16
- export type { AudioProperties, FileType, PropertyMap } from "./types.ts";
17
-
44
+ /**
45
+ * Represents an audio file with metadata and audio properties.
46
+ * Provides methods for reading and writing metadata, accessing audio properties,
47
+ * and managing format-specific features.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const file = await taglib.openFile(audioBuffer);
52
+ *
53
+ * // Check if valid
54
+ * if (!file.isValid()) {
55
+ * throw new Error("Invalid audio file");
56
+ * }
57
+ *
58
+ * // Read metadata
59
+ * const tag = file.tag();
60
+ * const props = file.audioProperties();
61
+ *
62
+ * // Modify and save
63
+ * tag.setTitle("New Title");
64
+ * file.save();
65
+ *
66
+ * // Get modified buffer
67
+ * const modifiedBuffer = file.getFileBuffer();
68
+ *
69
+ * // Clean up
70
+ * file.dispose();
71
+ * ```
72
+ */
18
73
  export interface AudioFile {
74
+ /**
75
+ * Get the audio file format.
76
+ * @returns The detected file type (e.g., "MP3", "FLAC", "MP4")
77
+ */
19
78
  getFormat(): FileType;
79
+
80
+ /**
81
+ * Get the tag object for reading/writing basic metadata.
82
+ * @returns Tag object with getters and setters for metadata fields
83
+ * @throws {Error} If unable to get tag from file
84
+ */
20
85
  tag(): Tag;
86
+
87
+ /**
88
+ * Get audio properties (duration, bitrate, sample rate, etc.).
89
+ * @returns Audio properties or null if unavailable
90
+ */
21
91
  audioProperties(): AudioProperties | null;
92
+
93
+ /**
94
+ * Get all metadata properties as a key-value map.
95
+ * Includes both standard and format-specific properties.
96
+ * @returns PropertyMap with all available metadata
97
+ */
22
98
  properties(): PropertyMap;
99
+
100
+ /**
101
+ * Set multiple properties at once from a PropertyMap.
102
+ * @param properties - Map of property names to values
103
+ */
23
104
  setProperties(properties: PropertyMap): void;
105
+
106
+ /**
107
+ * Get a single property value by key.
108
+ * @param key - Property name (e.g., "ALBUMARTIST", "ACOUSTID_ID")
109
+ * @returns Property value or undefined if not found
110
+ */
24
111
  getProperty(key: string): string | undefined;
112
+
113
+ /**
114
+ * Set a single property value.
115
+ * @param key - Property name
116
+ * @param value - Property value
117
+ */
25
118
  setProperty(key: string, value: string): void;
119
+
120
+ /**
121
+ * Check if this is an MP4/M4A file.
122
+ * @returns true if the file is MP4/M4A format
123
+ */
26
124
  isMP4(): boolean;
125
+
126
+ /**
127
+ * Get an MP4-specific metadata item.
128
+ * @param key - MP4 atom name (e.g., "----:com.apple.iTunes:iTunNORM")
129
+ * @returns Item value or undefined if not found
130
+ * @throws {Error} If not an MP4 file
131
+ */
27
132
  getMP4Item(key: string): string | undefined;
133
+
134
+ /**
135
+ * Set an MP4-specific metadata item.
136
+ * @param key - MP4 atom name
137
+ * @param value - Item value
138
+ * @throws {Error} If not an MP4 file
139
+ */
28
140
  setMP4Item(key: string, value: string): void;
141
+
142
+ /**
143
+ * Remove an MP4-specific metadata item.
144
+ * @param key - MP4 atom name to remove
145
+ * @throws {Error} If not an MP4 file
146
+ */
29
147
  removeMP4Item(key: string): void;
148
+
149
+ /**
150
+ * Save all changes to the in-memory buffer.
151
+ * Note: This does not write to disk, but updates the internal buffer.
152
+ * Use getFileBuffer() to retrieve the modified data.
153
+ * @returns true if save was successful
154
+ */
30
155
  save(): boolean;
156
+
157
+ /**
158
+ * Get the current file data as a buffer, including any modifications.
159
+ * Call this after save() to get the updated file data.
160
+ * @returns Uint8Array containing the complete file data
161
+ */
31
162
  getFileBuffer(): Uint8Array;
163
+
164
+ /**
165
+ * Check if the file was loaded successfully and is valid.
166
+ * @returns true if the file is valid and can be processed
167
+ */
32
168
  isValid(): boolean;
169
+
170
+ /**
171
+ * Release all resources associated with this file.
172
+ * Always call this when done to prevent memory leaks.
173
+ */
33
174
  dispose(): void;
34
175
  }
35
176
 
36
177
  /**
37
- * Audio file wrapper using Embind API
178
+ * Implementation of AudioFile interface using Embind API.
179
+ * Wraps the native TagLib C++ FileHandle object.
180
+ *
181
+ * @internal This class is not meant to be instantiated directly.
182
+ * Use TagLib.openFile() to create instances.
38
183
  */
39
184
  export class AudioFileImpl implements AudioFile {
40
185
  private fileHandle: any;
@@ -48,22 +193,18 @@ export class AudioFileImpl implements AudioFile {
48
193
  this.fileHandle = fileHandle;
49
194
  }
50
195
 
51
- /**
52
- * Get file format
53
- */
196
+ /** @inheritdoc */
54
197
  getFormat(): FileType {
55
198
  return this.fileHandle.getFormat() as FileType;
56
199
  }
57
200
 
58
- /**
59
- * Get tag object for reading/writing metadata
60
- */
201
+ /** @inheritdoc */
61
202
  tag(): Tag {
62
203
  const tagWrapper = this.fileHandle.getTag();
63
204
  if (!tagWrapper) {
64
205
  throw new Error("Failed to get tag from file");
65
206
  }
66
-
207
+
67
208
  return {
68
209
  title: tagWrapper.title(),
69
210
  artist: tagWrapper.artist(),
@@ -72,7 +213,7 @@ export class AudioFileImpl implements AudioFile {
72
213
  genre: tagWrapper.genre(),
73
214
  year: tagWrapper.year(),
74
215
  track: tagWrapper.track(),
75
-
216
+
76
217
  setTitle: (value: string) => tagWrapper.setTitle(value),
77
218
  setArtist: (value: string) => tagWrapper.setArtist(value),
78
219
  setAlbum: (value: string) => tagWrapper.setAlbum(value),
@@ -83,16 +224,14 @@ export class AudioFileImpl implements AudioFile {
83
224
  };
84
225
  }
85
226
 
86
- /**
87
- * Get audio properties
88
- */
227
+ /** @inheritdoc */
89
228
  audioProperties(): AudioProperties | null {
90
229
  if (!this.cachedAudioProperties) {
91
230
  const propsWrapper = this.fileHandle.getAudioProperties();
92
231
  if (!propsWrapper) {
93
232
  return null;
94
233
  }
95
-
234
+
96
235
  this.cachedAudioProperties = {
97
236
  length: propsWrapper.lengthInSeconds(),
98
237
  bitrate: propsWrapper.bitrate(),
@@ -100,58 +239,46 @@ export class AudioFileImpl implements AudioFile {
100
239
  channels: propsWrapper.channels(),
101
240
  };
102
241
  }
103
-
242
+
104
243
  return this.cachedAudioProperties;
105
244
  }
106
245
 
107
- /**
108
- * Get all properties as a PropertyMap
109
- */
246
+ /** @inheritdoc */
110
247
  properties(): PropertyMap {
111
248
  const jsObj = this.fileHandle.getProperties();
112
249
  const result: PropertyMap = {};
113
-
250
+
114
251
  // Convert from Emscripten val to plain object
115
252
  const keys = Object.keys(jsObj);
116
253
  for (const key of keys) {
117
254
  result[key] = jsObj[key];
118
255
  }
119
-
256
+
120
257
  return result;
121
258
  }
122
259
 
123
- /**
124
- * Set properties from a PropertyMap
125
- */
260
+ /** @inheritdoc */
126
261
  setProperties(properties: PropertyMap): void {
127
262
  this.fileHandle.setProperties(properties);
128
263
  }
129
264
 
130
- /**
131
- * Get a single property value
132
- */
265
+ /** @inheritdoc */
133
266
  getProperty(key: string): string | undefined {
134
267
  const value = this.fileHandle.getProperty(key);
135
268
  return value === "" ? undefined : value;
136
269
  }
137
270
 
138
- /**
139
- * Set a single property value
140
- */
271
+ /** @inheritdoc */
141
272
  setProperty(key: string, value: string): void {
142
273
  this.fileHandle.setProperty(key, value);
143
274
  }
144
275
 
145
- /**
146
- * Check if this is an MP4 file
147
- */
276
+ /** @inheritdoc */
148
277
  isMP4(): boolean {
149
278
  return this.fileHandle.isMP4();
150
279
  }
151
280
 
152
- /**
153
- * Get MP4-specific item
154
- */
281
+ /** @inheritdoc */
155
282
  getMP4Item(key: string): string | undefined {
156
283
  if (!this.isMP4()) {
157
284
  throw new Error("Not an MP4 file");
@@ -160,9 +287,7 @@ export class AudioFileImpl implements AudioFile {
160
287
  return value === "" ? undefined : value;
161
288
  }
162
289
 
163
- /**
164
- * Set MP4-specific item
165
- */
290
+ /** @inheritdoc */
166
291
  setMP4Item(key: string, value: string): void {
167
292
  if (!this.isMP4()) {
168
293
  throw new Error("Not an MP4 file");
@@ -170,9 +295,7 @@ export class AudioFileImpl implements AudioFile {
170
295
  this.fileHandle.setMP4Item(key, value);
171
296
  }
172
297
 
173
- /**
174
- * Remove MP4-specific item
175
- */
298
+ /** @inheritdoc */
176
299
  removeMP4Item(key: string): void {
177
300
  if (!this.isMP4()) {
178
301
  throw new Error("Not an MP4 file");
@@ -180,26 +303,22 @@ export class AudioFileImpl implements AudioFile {
180
303
  this.fileHandle.removeMP4Item(key);
181
304
  }
182
305
 
183
- /**
184
- * Save changes to the file
185
- */
306
+ /** @inheritdoc */
186
307
  save(): boolean {
187
308
  // Clear caches since values may have changed
188
309
  this.cachedTag = null;
189
310
  this.cachedAudioProperties = null;
190
-
311
+
191
312
  return this.fileHandle.save();
192
313
  }
193
314
 
194
- /**
195
- * Get the current file buffer after modifications
196
- */
315
+ /** @inheritdoc */
197
316
  getFileBuffer(): Uint8Array {
198
317
  const bufferString = this.fileHandle.getBuffer();
199
318
  if (!bufferString) {
200
319
  return new Uint8Array(0);
201
320
  }
202
-
321
+
203
322
  // Convert string to Uint8Array
204
323
  const buffer = new Uint8Array(bufferString.length);
205
324
  for (let i = 0; i < bufferString.length; i++) {
@@ -208,16 +327,12 @@ export class AudioFileImpl implements AudioFile {
208
327
  return buffer;
209
328
  }
210
329
 
211
- /**
212
- * Check if the file is valid
213
- */
330
+ /** @inheritdoc */
214
331
  isValid(): boolean {
215
332
  return this.fileHandle.isValid();
216
333
  }
217
334
 
218
- /**
219
- * Free resources
220
- */
335
+ /** @inheritdoc */
221
336
  dispose(): void {
222
337
  if (this.fileHandle) {
223
338
  // Embind will handle cleanup when the object goes out of scope
@@ -230,7 +345,25 @@ export class AudioFileImpl implements AudioFile {
230
345
  }
231
346
 
232
347
  /**
233
- * Main TagLib interface using Embind
348
+ * Main TagLib interface for audio metadata operations.
349
+ * Provides methods to open audio files and access TagLib functionality.
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * // Option 1: Auto-initialize
354
+ * const taglib = await TagLib.initialize();
355
+ *
356
+ * // Option 2: Manual initialization
357
+ * import { loadTagLibModule } from "taglib-wasm";
358
+ * const module = await loadTagLibModule();
359
+ * const taglib = new TagLib(module);
360
+ *
361
+ * // Open and process a file
362
+ * const file = await taglib.openFile(audioBuffer);
363
+ * const tag = file.tag();
364
+ * console.log(`Title: ${tag.title}`);
365
+ * file.dispose();
366
+ * ```
234
367
  */
235
368
  export class TagLib {
236
369
  private module: TagLibModule;
@@ -240,7 +373,16 @@ export class TagLib {
240
373
  }
241
374
 
242
375
  /**
243
- * Initialize TagLib with default configuration
376
+ * Initialize TagLib with default configuration.
377
+ * This is the recommended way to create a TagLib instance.
378
+ *
379
+ * @returns Promise resolving to initialized TagLib instance
380
+ *
381
+ * @example
382
+ * ```typescript
383
+ * const taglib = await TagLib.initialize();
384
+ * const file = await taglib.openFile(buffer);
385
+ * ```
244
386
  */
245
387
  static async initialize(): Promise<TagLib> {
246
388
  // Use the loadTagLibModule function
@@ -250,31 +392,53 @@ export class TagLib {
250
392
  }
251
393
 
252
394
  /**
253
- * Open a file from a buffer
395
+ * Open an audio file from a buffer.
396
+ * Automatically detects the file format based on content.
397
+ *
398
+ * @param buffer - Audio file data as ArrayBuffer or typed array
399
+ * @returns Promise resolving to AudioFile instance
400
+ * @throws {Error} If the file format is invalid or unsupported
401
+ * @throws {Error} If the module is not properly initialized
402
+ *
403
+ * @example
404
+ * ```typescript
405
+ * // From ArrayBuffer
406
+ * const file = await taglib.openFile(arrayBuffer);
407
+ *
408
+ * // From Uint8Array
409
+ * const uint8Array = new Uint8Array(buffer);
410
+ * const file = await taglib.openFile(uint8Array.buffer);
411
+ *
412
+ * // Remember to dispose when done
413
+ * file.dispose();
414
+ * ```
254
415
  */
255
416
  async openFile(buffer: ArrayBuffer): Promise<AudioFile> {
256
417
  // Check if Embind is available
257
418
  if (!this.module.createFileHandle) {
258
- throw new Error("TagLib module not properly initialized - createFileHandle not found");
419
+ throw new Error(
420
+ "TagLib module not properly initialized - createFileHandle not found",
421
+ );
259
422
  }
260
-
423
+
261
424
  // Convert ArrayBuffer to Uint8Array for Embind
262
425
  const uint8Array = new Uint8Array(buffer);
263
-
426
+
264
427
  // Create a new FileHandle
265
428
  const fileHandle = this.module.createFileHandle();
266
-
429
+
267
430
  // Load the buffer - Embind should handle Uint8Array conversion
268
431
  const success = fileHandle.loadFromBuffer(uint8Array);
269
432
  if (!success) {
270
433
  throw new Error("Failed to load file from buffer");
271
434
  }
272
-
435
+
273
436
  return new AudioFileImpl(this.module, fileHandle);
274
437
  }
275
438
 
276
439
  /**
277
- * Get version information
440
+ * Get the TagLib version.
441
+ * @returns Version string (e.g., "2.1.0")
278
442
  */
279
443
  version(): string {
280
444
  return "2.1.0"; // TagLib version we're using
@@ -282,8 +446,20 @@ export class TagLib {
282
446
  }
283
447
 
284
448
  /**
285
- * Create a TagLib instance
449
+ * Create a TagLib instance from a pre-loaded Wasm module.
450
+ * For advanced users who need custom module configuration.
451
+ *
452
+ * @param module - Pre-loaded Wasm module from loadTagLibModule()
453
+ * @returns Promise resolving to TagLib instance
454
+ *
455
+ * @example
456
+ * ```typescript
457
+ * import { loadTagLibModule, createTagLib } from "taglib-wasm";
458
+ *
459
+ * const module = await loadTagLibModule();
460
+ * const taglib = await createTagLib(module);
461
+ * ```
286
462
  */
287
463
  export async function createTagLib(module: WasmModule): Promise<TagLib> {
288
464
  return new TagLib(module);
289
- }
465
+ }