taglib-wasm 0.2.7 → 0.2.8
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/README.md +18 -52
- package/build/taglib.js +4 -10
- package/build/taglib.wasm +0 -0
- package/index.ts +13 -3
- package/package.json +3 -1
- package/src/simple-jsr.ts +40 -37
- package/src/simple.ts +19 -22
- package/src/taglib-jsr.ts +22 -518
- package/src/taglib.ts +160 -505
- package/src/types.ts +6 -3
- package/src/wasm-jsr.ts +12 -279
- package/src/wasm.ts +75 -244
- package/src/enhanced-api.ts +0 -296
- package/src/taglib-embind.ts +0 -231
- package/src/wasm-embind.ts +0 -55
package/src/taglib-jsr.ts
CHANGED
|
@@ -5,540 +5,44 @@
|
|
|
5
5
|
* to be compatible with JSR publishing requirements.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
TagLibConfig,
|
|
17
|
-
} from "./types.ts";
|
|
18
|
-
import {
|
|
19
|
-
cStringToJSJSR,
|
|
20
|
-
jsToCStringJSR,
|
|
21
|
-
loadTagLibModuleJSR,
|
|
22
|
-
type TagLibModule,
|
|
23
|
-
} from "./wasm-jsr.ts";
|
|
24
|
-
import {
|
|
25
|
-
BITRATE_CONTROL_MODE_NAMES,
|
|
26
|
-
BITRATE_CONTROL_MODE_VALUES,
|
|
27
|
-
METADATA_MAPPINGS
|
|
28
|
-
} from "./types.ts";
|
|
8
|
+
import { AudioFileImpl, TagLib as TagLibBase, createTagLib } from "./taglib.ts";
|
|
9
|
+
import type { AudioFile, Tag, AudioProperties, FileType, PropertyMap } from "./taglib.ts";
|
|
10
|
+
import type { TagLibConfig } from "./types.ts";
|
|
11
|
+
import type { WasmModule } from "./wasm-jsr.ts";
|
|
12
|
+
|
|
13
|
+
// Re-export types and implementations
|
|
14
|
+
export { AudioFileImpl as AudioFileJSR } from "./taglib.ts";
|
|
15
|
+
export type { Tag, AudioProperties, FileType, PropertyMap } from "./taglib.ts";
|
|
29
16
|
|
|
30
17
|
/**
|
|
31
18
|
* JSR-compatible TagLib singleton for WASM module management
|
|
32
19
|
*/
|
|
33
|
-
export class TagLibJSR {
|
|
20
|
+
export class TagLibJSR extends TagLibBase {
|
|
34
21
|
private static instance: TagLibJSR | null = null;
|
|
35
|
-
private
|
|
36
|
-
private initialized = false;
|
|
37
|
-
|
|
38
|
-
private constructor() {}
|
|
22
|
+
private static initialized = false;
|
|
39
23
|
|
|
40
|
-
static getInstance(): TagLibJSR {
|
|
24
|
+
static async getInstance(): Promise<TagLibJSR> {
|
|
41
25
|
if (!TagLibJSR.instance) {
|
|
42
|
-
|
|
26
|
+
// For JSR, we need to load the module directly without Node.js dependencies
|
|
27
|
+
// This is a placeholder - the actual implementation would need to load WASM directly
|
|
28
|
+
throw new Error("JSR version requires direct WASM loading implementation. Please use the NPM version for now.");
|
|
43
29
|
}
|
|
44
30
|
return TagLibJSR.instance;
|
|
45
31
|
}
|
|
46
32
|
|
|
47
|
-
async initialize(config?: TagLibConfig): Promise<void> {
|
|
48
|
-
if (this.initialized) return;
|
|
49
|
-
|
|
50
|
-
this.module = await loadTagLibModuleJSR(config);
|
|
51
|
-
this.initialized = true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
getModule(): TagLibModule {
|
|
55
|
-
if (!this.module) {
|
|
56
|
-
throw new Error(
|
|
57
|
-
"TagLib not initialized. Call TagLib.initialize() first.",
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
return this.module;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
isInitialized(): boolean {
|
|
64
|
-
return this.initialized;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Initialize taglib-wasm module
|
|
69
|
-
*/
|
|
70
33
|
static async initialize(config?: TagLibConfig): Promise<void> {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Get the WASM module instance
|
|
77
|
-
*/
|
|
78
|
-
static getModule(): TagLibModule {
|
|
79
|
-
return TagLibJSR.getInstance().getModule();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Check if TagLib is initialized
|
|
84
|
-
*/
|
|
85
|
-
static isInitialized(): boolean {
|
|
86
|
-
return TagLibJSR.getInstance().isInitialized();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* JSR-compatible audio file handling class
|
|
92
|
-
*/
|
|
93
|
-
export class AudioFileJSR {
|
|
94
|
-
private fileId: number = 0;
|
|
95
|
-
private module: TagLibModule;
|
|
96
|
-
private dataPtr: number = 0;
|
|
97
|
-
|
|
98
|
-
constructor(data: Uint8Array) {
|
|
99
|
-
this.module = TagLibJSR.getModule();
|
|
100
|
-
|
|
101
|
-
// Allocate memory for file data
|
|
102
|
-
this.dataPtr = this.module.allocate(data, this.module.ALLOC_NORMAL);
|
|
103
|
-
|
|
104
|
-
// Create TagLib file from buffer
|
|
105
|
-
this.fileId = this.module._taglib_file_new_from_buffer(
|
|
106
|
-
this.dataPtr,
|
|
107
|
-
data.length,
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
if (!this.isValid()) {
|
|
111
|
-
this.cleanup();
|
|
112
|
-
throw new Error(
|
|
113
|
-
"Failed to load audio file - invalid format or corrupted data",
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Check if the file is valid
|
|
120
|
-
*/
|
|
121
|
-
isValid(): boolean {
|
|
122
|
-
return this.module._taglib_file_is_valid(this.fileId) === 1;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Get the audio format
|
|
127
|
-
*/
|
|
128
|
-
getFormat(): AudioFormat {
|
|
129
|
-
const formatId = this.module._taglib_file_format(this.fileId);
|
|
130
|
-
const formats: Record<number, AudioFormat> = {
|
|
131
|
-
1: "MP3",
|
|
132
|
-
2: "FLAC",
|
|
133
|
-
3: "OGG",
|
|
134
|
-
4: "MP4",
|
|
135
|
-
5: "WMA",
|
|
136
|
-
6: "APE",
|
|
137
|
-
7: "MPC",
|
|
138
|
-
8: "WV",
|
|
139
|
-
9: "OPUS",
|
|
140
|
-
10: "TTA",
|
|
141
|
-
11: "WAV",
|
|
142
|
-
12: "AIFF",
|
|
143
|
-
13: "MOD",
|
|
144
|
-
14: "S3M",
|
|
145
|
-
15: "XM",
|
|
146
|
-
16: "IT",
|
|
147
|
-
};
|
|
148
|
-
return formats[formatId] || "MP3";
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Get basic tag information
|
|
153
|
-
*/
|
|
154
|
-
getTag(): Tag {
|
|
155
|
-
const tagPtr = this.module._taglib_file_tag(this.fileId);
|
|
156
|
-
if (tagPtr === 0) {
|
|
157
|
-
return {
|
|
158
|
-
title: "",
|
|
159
|
-
artist: "",
|
|
160
|
-
album: "",
|
|
161
|
-
comment: "",
|
|
162
|
-
genre: "",
|
|
163
|
-
year: 0,
|
|
164
|
-
track: 0,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
title: cStringToJSJSR(this.module, this.module._taglib_tag_title(tagPtr)),
|
|
170
|
-
artist: cStringToJSJSR(
|
|
171
|
-
this.module,
|
|
172
|
-
this.module._taglib_tag_artist(tagPtr),
|
|
173
|
-
),
|
|
174
|
-
album: cStringToJSJSR(this.module, this.module._taglib_tag_album(tagPtr)),
|
|
175
|
-
comment: cStringToJSJSR(
|
|
176
|
-
this.module,
|
|
177
|
-
this.module._taglib_tag_comment(tagPtr),
|
|
178
|
-
),
|
|
179
|
-
genre: cStringToJSJSR(this.module, this.module._taglib_tag_genre(tagPtr)),
|
|
180
|
-
year: this.module._taglib_tag_year(tagPtr),
|
|
181
|
-
track: this.module._taglib_tag_track(tagPtr),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Set basic tag information
|
|
187
|
-
*/
|
|
188
|
-
setTag(tag: Partial<Tag>): void {
|
|
189
|
-
const tagPtr = this.module._taglib_file_tag(this.fileId);
|
|
190
|
-
if (tagPtr === 0) return;
|
|
191
|
-
|
|
192
|
-
if (tag.title !== undefined) {
|
|
193
|
-
const titlePtr = jsToCStringJSR(this.module, tag.title);
|
|
194
|
-
this.module._taglib_tag_set_title(tagPtr, titlePtr);
|
|
195
|
-
this.module._free(titlePtr);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (tag.artist !== undefined) {
|
|
199
|
-
const artistPtr = jsToCStringJSR(this.module, tag.artist);
|
|
200
|
-
this.module._taglib_tag_set_artist(tagPtr, artistPtr);
|
|
201
|
-
this.module._free(artistPtr);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (tag.album !== undefined) {
|
|
205
|
-
const albumPtr = jsToCStringJSR(this.module, tag.album);
|
|
206
|
-
this.module._taglib_tag_set_album(tagPtr, albumPtr);
|
|
207
|
-
this.module._free(albumPtr);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (tag.comment !== undefined) {
|
|
211
|
-
const commentPtr = jsToCStringJSR(this.module, tag.comment);
|
|
212
|
-
this.module._taglib_tag_set_comment(tagPtr, commentPtr);
|
|
213
|
-
this.module._free(commentPtr);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (tag.genre !== undefined) {
|
|
217
|
-
const genrePtr = jsToCStringJSR(this.module, tag.genre);
|
|
218
|
-
this.module._taglib_tag_set_genre(tagPtr, genrePtr);
|
|
219
|
-
this.module._free(genrePtr);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (tag.year !== undefined) {
|
|
223
|
-
this.module._taglib_tag_set_year(tagPtr, tag.year);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (tag.track !== undefined) {
|
|
227
|
-
this.module._taglib_tag_set_track(tagPtr, tag.track);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Get audio properties
|
|
233
|
-
*/
|
|
234
|
-
getAudioProperties(): AudioProperties {
|
|
235
|
-
const propsPtr = this.module._taglib_file_audioproperties(this.fileId);
|
|
236
|
-
if (propsPtr === 0) {
|
|
237
|
-
return {
|
|
238
|
-
length: 0,
|
|
239
|
-
bitrate: 0,
|
|
240
|
-
sampleRate: 0,
|
|
241
|
-
channels: 0,
|
|
242
|
-
format: this.getFormat(),
|
|
243
|
-
};
|
|
34
|
+
if (!TagLibJSR.initialized) {
|
|
35
|
+
await TagLibJSR.getInstance();
|
|
244
36
|
}
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
length: this.module._taglib_audioproperties_length(propsPtr),
|
|
248
|
-
bitrate: this.module._taglib_audioproperties_bitrate(propsPtr),
|
|
249
|
-
sampleRate: this.module._taglib_audioproperties_samplerate(propsPtr),
|
|
250
|
-
channels: this.module._taglib_audioproperties_channels(propsPtr),
|
|
251
|
-
format: this.getFormat(),
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get extended tag information (placeholder for JSR version)
|
|
257
|
-
*/
|
|
258
|
-
getExtendedTag(): ExtendedTag {
|
|
259
|
-
// For JSR compatibility, return basic implementation
|
|
260
|
-
const basicTag = this.getTag();
|
|
261
|
-
return {
|
|
262
|
-
...basicTag,
|
|
263
|
-
|
|
264
|
-
// Fingerprinting & identification - placeholder
|
|
265
|
-
acoustidFingerprint: "",
|
|
266
|
-
acoustidId: "",
|
|
267
|
-
musicbrainzTrackId: "",
|
|
268
|
-
musicbrainzReleaseId: "",
|
|
269
|
-
musicbrainzArtistId: "",
|
|
270
|
-
musicbrainzReleaseGroupId: "",
|
|
271
|
-
albumArtist: "",
|
|
272
|
-
composer: "",
|
|
273
|
-
discNumber: 0,
|
|
274
|
-
totalTracks: 0,
|
|
275
|
-
totalDiscs: 0,
|
|
276
|
-
bpm: 0,
|
|
277
|
-
compilation: false,
|
|
278
|
-
titleSort: "",
|
|
279
|
-
artistSort: "",
|
|
280
|
-
albumSort: "",
|
|
281
|
-
|
|
282
|
-
// Volume normalization - placeholder
|
|
283
|
-
replayGainTrackGain: "",
|
|
284
|
-
replayGainTrackPeak: "",
|
|
285
|
-
replayGainAlbumGain: "",
|
|
286
|
-
replayGainAlbumPeak: "",
|
|
287
|
-
appleSoundCheck: "",
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Set extended tag fields (placeholder for JSR version)
|
|
293
|
-
*/
|
|
294
|
-
setExtendedTag(extendedTag: Partial<ExtendedTag>): void {
|
|
295
|
-
// Set basic fields
|
|
296
|
-
this.setTag(extendedTag);
|
|
297
|
-
|
|
298
|
-
// Extended fields are placeholder for JSR version
|
|
299
|
-
// Full implementation would require PropertyMap integration
|
|
300
37
|
}
|
|
301
38
|
|
|
302
39
|
/**
|
|
303
|
-
*
|
|
40
|
+
* Open a file from a buffer (JSR-compatible version)
|
|
304
41
|
*/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return [];
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Set pictures in the file (placeholder for JSR version)
|
|
312
|
-
*/
|
|
313
|
-
setPictures(pictures: Picture[]): void {
|
|
314
|
-
// Placeholder - full implementation would require format-specific picture handling
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Placeholder methods for extended metadata (same as main implementation)
|
|
318
|
-
getAcoustidFingerprint(): string {
|
|
319
|
-
return "";
|
|
320
|
-
}
|
|
321
|
-
setAcoustidFingerprint(fingerprint: string): void {}
|
|
322
|
-
getMusicbrainzTrackId(): string {
|
|
323
|
-
return "";
|
|
324
|
-
}
|
|
325
|
-
setMusicbrainzTrackId(id: string): void {}
|
|
326
|
-
getMusicbrainzRecordingId(): string {
|
|
327
|
-
return "";
|
|
328
|
-
}
|
|
329
|
-
setMusicbrainzRecordingId(id: string): void {}
|
|
330
|
-
getMusicbrainzArtistId(): string {
|
|
331
|
-
return "";
|
|
332
|
-
}
|
|
333
|
-
setMusicbrainzArtistId(id: string): void {}
|
|
334
|
-
getMusicbrainzAlbumId(): string {
|
|
335
|
-
return "";
|
|
336
|
-
}
|
|
337
|
-
setMusicbrainzAlbumId(id: string): void {}
|
|
338
|
-
getMusicbrainzAlbumArtistId(): string {
|
|
339
|
-
return "";
|
|
340
|
-
}
|
|
341
|
-
setMusicbrainzAlbumArtistId(id: string): void {}
|
|
342
|
-
getMusicbrainzReleaseGroupId(): string {
|
|
343
|
-
return "";
|
|
344
|
-
}
|
|
345
|
-
setMusicbrainzReleaseGroupId(id: string): void {}
|
|
346
|
-
getReplayGainTrackGain(): string {
|
|
347
|
-
return "";
|
|
348
|
-
}
|
|
349
|
-
setReplayGainTrackGain(gain: string): void {}
|
|
350
|
-
getReplayGainTrackPeak(): string {
|
|
351
|
-
return "";
|
|
352
|
-
}
|
|
353
|
-
setReplayGainTrackPeak(peak: string): void {}
|
|
354
|
-
getReplayGainAlbumGain(): string {
|
|
355
|
-
return "";
|
|
356
|
-
}
|
|
357
|
-
setReplayGainAlbumGain(gain: string): void {}
|
|
358
|
-
getReplayGainAlbumPeak(): string {
|
|
359
|
-
return "";
|
|
360
|
-
}
|
|
361
|
-
setReplayGainAlbumPeak(peak: string): void {}
|
|
362
|
-
getAppleSoundCheck(): string {
|
|
363
|
-
return "";
|
|
364
|
-
}
|
|
365
|
-
setAppleSoundCheck(soundCheck: string): void {}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Get all properties as a PropertyMap
|
|
369
|
-
*/
|
|
370
|
-
properties(): PropertyMap {
|
|
371
|
-
const jsonPtr = this.module._taglib_file_properties_json(this.fileId);
|
|
372
|
-
if (jsonPtr === 0) return {};
|
|
373
|
-
|
|
374
|
-
const jsonStr = cStringToJSJSR(this.module, jsonPtr);
|
|
375
|
-
try {
|
|
376
|
-
return JSON.parse(jsonStr);
|
|
377
|
-
} catch {
|
|
378
|
-
return {};
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Set properties from a PropertyMap
|
|
384
|
-
*/
|
|
385
|
-
setProperties(properties: PropertyMap): boolean {
|
|
386
|
-
const jsonStr = JSON.stringify(properties);
|
|
387
|
-
const jsonPtr = jsToCStringJSR(this.module, jsonStr);
|
|
388
|
-
try {
|
|
389
|
-
return this.module._taglib_file_set_properties_json(this.fileId, jsonPtr) !== 0;
|
|
390
|
-
} finally {
|
|
391
|
-
this.module._free(jsonPtr);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Get a specific property value
|
|
397
|
-
*/
|
|
398
|
-
getProperty(key: string): string[] | undefined {
|
|
399
|
-
const keyPtr = jsToCStringJSR(this.module, key);
|
|
400
|
-
try {
|
|
401
|
-
const valuePtr = this.module._taglib_file_get_property(this.fileId, keyPtr);
|
|
402
|
-
if (valuePtr === 0) return undefined;
|
|
403
|
-
|
|
404
|
-
const value = cStringToJSJSR(this.module, valuePtr);
|
|
405
|
-
return value ? [value] : undefined;
|
|
406
|
-
} finally {
|
|
407
|
-
this.module._free(keyPtr);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Set a specific property value
|
|
413
|
-
*/
|
|
414
|
-
setProperty(key: string, values: string | string[]): boolean {
|
|
415
|
-
const value = Array.isArray(values) ? values[0] : values;
|
|
416
|
-
if (!value) return false;
|
|
417
|
-
|
|
418
|
-
const keyPtr = jsToCStringJSR(this.module, key);
|
|
419
|
-
const valuePtr = jsToCStringJSR(this.module, value);
|
|
420
|
-
try {
|
|
421
|
-
return this.module._taglib_file_set_property(this.fileId, keyPtr, valuePtr) !== 0;
|
|
422
|
-
} finally {
|
|
423
|
-
this.module._free(keyPtr);
|
|
424
|
-
this.module._free(valuePtr);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Check if this is an MP4 file
|
|
430
|
-
*/
|
|
431
|
-
isMP4(): boolean {
|
|
432
|
-
return this.module._taglib_file_is_mp4(this.fileId) !== 0;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Get MP4-specific item (for custom atoms)
|
|
437
|
-
*/
|
|
438
|
-
getMP4Item(key: string): string | undefined {
|
|
439
|
-
if (!this.isMP4()) return undefined;
|
|
440
|
-
|
|
441
|
-
const keyPtr = jsToCStringJSR(this.module, key);
|
|
442
|
-
try {
|
|
443
|
-
const valuePtr = this.module._taglib_mp4_get_item(this.fileId, keyPtr);
|
|
444
|
-
if (valuePtr === 0) return undefined;
|
|
445
|
-
|
|
446
|
-
return cStringToJSJSR(this.module, valuePtr);
|
|
447
|
-
} finally {
|
|
448
|
-
this.module._free(keyPtr);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Set MP4-specific item (for custom atoms)
|
|
454
|
-
*/
|
|
455
|
-
setMP4Item(key: string, value: string): boolean {
|
|
456
|
-
if (!this.isMP4()) return false;
|
|
457
|
-
|
|
458
|
-
const keyPtr = jsToCStringJSR(this.module, key);
|
|
459
|
-
const valuePtr = jsToCStringJSR(this.module, value);
|
|
460
|
-
try {
|
|
461
|
-
return this.module._taglib_mp4_set_item(this.fileId, keyPtr, valuePtr) !== 0;
|
|
462
|
-
} finally {
|
|
463
|
-
this.module._free(keyPtr);
|
|
464
|
-
this.module._free(valuePtr);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Remove MP4-specific item
|
|
470
|
-
*/
|
|
471
|
-
removeMP4Item(key: string): boolean {
|
|
472
|
-
if (!this.isMP4()) return false;
|
|
473
|
-
|
|
474
|
-
const keyPtr = jsToCStringJSR(this.module, key);
|
|
475
|
-
try {
|
|
476
|
-
return this.module._taglib_mp4_remove_item(this.fileId, keyPtr) !== 0;
|
|
477
|
-
} finally {
|
|
478
|
-
this.module._free(keyPtr);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Get bitrate control mode (MP4/M4A specific)
|
|
484
|
-
* Reads from the 'acbf' atom
|
|
485
|
-
*/
|
|
486
|
-
getBitrateControlMode(): BitrateControlMode | undefined {
|
|
487
|
-
if (!this.isMP4()) return undefined;
|
|
488
|
-
|
|
489
|
-
const value = this.getMP4Item("acbf");
|
|
490
|
-
if (!value) return undefined;
|
|
491
|
-
|
|
492
|
-
const numValue = parseInt(value, 10);
|
|
493
|
-
if (isNaN(numValue) || numValue < 0 || numValue > 3) return undefined;
|
|
494
|
-
|
|
495
|
-
return BITRATE_CONTROL_MODE_NAMES[numValue];
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Set bitrate control mode (MP4/M4A specific)
|
|
500
|
-
* Writes to the 'acbf' atom
|
|
501
|
-
*/
|
|
502
|
-
setBitrateControlMode(mode: BitrateControlMode): boolean {
|
|
503
|
-
if (!this.isMP4()) return false;
|
|
504
|
-
|
|
505
|
-
const numValue = BITRATE_CONTROL_MODE_VALUES[mode];
|
|
506
|
-
if (numValue === undefined) return false;
|
|
507
|
-
|
|
508
|
-
return this.setMP4Item("acbf", numValue.toString());
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Save changes to the file
|
|
513
|
-
*/
|
|
514
|
-
save(): Uint8Array {
|
|
515
|
-
if (!this.module._taglib_file_save(this.fileId)) {
|
|
516
|
-
throw new Error("Failed to save audio file");
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// In a real implementation, we'd need to extract the modified file data
|
|
520
|
-
// For now, return empty array as placeholder
|
|
521
|
-
return new Uint8Array(0);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Clean up resources
|
|
526
|
-
*/
|
|
527
|
-
cleanup(): void {
|
|
528
|
-
if (this.fileId) {
|
|
529
|
-
this.module._taglib_file_delete(this.fileId);
|
|
530
|
-
this.fileId = 0;
|
|
531
|
-
}
|
|
532
|
-
if (this.dataPtr) {
|
|
533
|
-
this.module._free(this.dataPtr);
|
|
534
|
-
this.dataPtr = 0;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Destructor to ensure cleanup
|
|
540
|
-
*/
|
|
541
|
-
destroy(): void {
|
|
542
|
-
this.cleanup();
|
|
42
|
+
override async openFile(buffer: ArrayBuffer): Promise<AudioFile> {
|
|
43
|
+
return super.openFile(buffer);
|
|
543
44
|
}
|
|
544
45
|
}
|
|
46
|
+
|
|
47
|
+
// Export alias for compatibility
|
|
48
|
+
export { TagLibJSR as TagLib };
|