taglib-wasm 0.2.4 → 0.2.6

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 CHANGED
@@ -5,7 +5,7 @@
5
5
  `taglib-wasm` is designed to be **TagLib for JavaScript/TypeScript** platforms — specifically Deno, Node.js, Bun, web browsers, and Cloudflare Workers. It does this by leveraging technologies including [TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and [Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
6
6
 
7
7
  > [!NOTE]
8
- > I’m personally using this to solve a problem for another project I’m creating, but this project is still very much a baby. You may experience tantrums at this stage of `taglib-wasm`’s development.
8
+ > This project is a baby, and you’re likely to experience some surprises at this stage of its development. I’m extremely moditivated to help address them, though.
9
9
 
10
10
  ## Why?
11
11
 
@@ -22,6 +22,7 @@ In the process of building a utility to improve the metadata of my music collect
22
22
  - **✅ Zero dependencies** – Self-contained WASM bundle
23
23
  - **✅ Memory efficient** – In-memory processing without filesystem access
24
24
  - **✅ Production ready** – Growing test suite helps ensure safety and reliability
25
+ - **🆕 Two API styles** – Choose between Simple (3 functions) or Core (full control) APIs
25
26
 
26
27
  ## 📦 Installation
27
28
 
@@ -36,19 +37,46 @@ import { TagLib } from "jsr:@charleswiltgen/taglib-wasm";
36
37
  ```bash
37
38
  npm install taglib-wasm
38
39
  ```
40
+
39
41
  ### Bun
40
42
 
41
43
  ```bash
42
44
  bun add taglib-wasm
43
45
  ```
46
+
44
47
  ## 🚀 Quick Start
45
48
 
46
- ### Deno
49
+ ### Simple API
50
+
51
+ Inspired by [go-taglib](https://github.com/sentriz/go-taglib)’s excellent developer experience:
52
+
53
+ ```typescript
54
+ import { readProperties, readTags, writeTags } from "taglib-wasm/simple";
55
+
56
+ // Read tags - just one function call!
57
+ const tags = await readTags("song.mp3");
58
+ console.log(tags.title, tags.artist, tags.album);
59
+
60
+ // Write tags - simple as can be
61
+ await writeTags("song.mp3", {
62
+ title: "New Title",
63
+ artist: "New Artist",
64
+ album: "New Album",
65
+ });
66
+
67
+ // Read audio properties
68
+ const props = await readProperties("song.mp3");
69
+ console.log(`Duration: ${props.length}s, Bitrate: ${props.bitrate} kbps`);
70
+ ```
71
+
72
+ ### Core API
73
+
74
+ Full control when you need it:
47
75
 
48
76
  ```typescript
49
77
  import { TagLib } from "jsr:@charleswiltgen/taglib-wasm";
50
78
 
51
- // Initialize TagLib WASM
79
+ // Initialize taglib-wasm
52
80
  const taglib = await TagLib.initialize();
53
81
 
54
82
  // Load audio file from buffer
@@ -80,17 +108,19 @@ file.setMusicBrainzTrackId("f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab");
80
108
  file.dispose();
81
109
  ```
82
110
 
83
- ### Bun
111
+ ## Platform examples
112
+
113
+ ### Deno
84
114
 
85
115
  ```typescript
86
- import { TagLib } from "taglib-wasm";
116
+ import { TagLib } from "jsr:@charleswiltgen/taglib-wasm";
87
117
 
88
- // Initialize TagLib WASM
118
+ // Initialize taglib-wasm
89
119
  const taglib = await TagLib.initialize();
90
120
 
91
- // Load from file system (Bun's native file API)
92
- const audioData = await Bun.file("song.mp3").arrayBuffer();
93
- const file = taglib.openFile(new Uint8Array(audioData));
121
+ // Load audio file from filesystem
122
+ const audioData = await Deno.readFile("song.mp3");
123
+ const file = taglib.openFile(audioData);
94
124
 
95
125
  // Read metadata
96
126
  const tags = file.tag();
@@ -123,7 +153,7 @@ file.dispose();
123
153
  import { TagLib } from "taglib-wasm";
124
154
  import { readFile } from "fs/promises";
125
155
 
126
- // Initialize TagLib WASM
156
+ // Initialize taglib-wasm
127
157
  const taglib = await TagLib.initialize();
128
158
 
129
159
  // Load audio file from filesystem
@@ -155,12 +185,49 @@ file.setMusicBrainzTrackId("f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab");
155
185
  file.dispose();
156
186
  ```
157
187
 
188
+ ### Bun
189
+
190
+ ```typescript
191
+ import { TagLib } from "taglib-wasm";
192
+
193
+ // Initialize taglib-wasm
194
+ const taglib = await TagLib.initialize();
195
+
196
+ // Load from file system (Bun's native file API)
197
+ const audioData = await Bun.file("song.mp3").arrayBuffer();
198
+ const file = taglib.openFile(new Uint8Array(audioData));
199
+
200
+ // Read metadata
201
+ const tags = file.tag();
202
+ const props = file.audioProperties();
203
+
204
+ console.log(`Title: ${tags.title}`);
205
+ console.log(`Artist: ${tags.artist}`);
206
+ console.log(`Duration: ${props.length}s`);
207
+ console.log(`Bitrate: ${props.bitrate} kbps`);
208
+
209
+ // Write metadata
210
+ file.setTitle("New Title");
211
+ file.setArtist("New Artist");
212
+ file.setAlbum("New Album");
213
+
214
+ console.log("Updated tags:", file.tag());
215
+
216
+ // Automatic tag mapping (format-agnostic)
217
+ file.setAcoustidFingerprint("AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...");
218
+ file.setAcoustidId("e7359e88-f1f7-41ed-b9f6-16e58e906997");
219
+ file.setMusicBrainzTrackId("f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab");
220
+
221
+ // Clean up
222
+ file.dispose();
223
+ ```
224
+
158
225
  ### Browser
159
226
 
160
227
  ```typescript
161
228
  import { TagLib } from "taglib-wasm";
162
229
 
163
- // Initialize TagLib WASM
230
+ // Initialize taglib-wasm
164
231
  const taglib = await TagLib.initialize();
165
232
 
166
233
  // Load from file input or fetch
@@ -203,7 +270,7 @@ export default {
203
270
  async fetch(request: Request): Promise<Response> {
204
271
  if (request.method === "POST") {
205
272
  try {
206
- // Initialize TagLib WASM
273
+ // Initialize taglib-wasm
207
274
  const taglib = await TagLib.initialize({
208
275
  memory: { initial: 8 * 1024 * 1024 }, // 8MB for Workers
209
276
  });
@@ -262,7 +329,7 @@ All formats are **fully tested and working**:
262
329
 
263
330
  ## 🎯 Automatic Tag Mapping
264
331
 
265
- TagLib WASM supports **automatic tag mapping** so you don’t have to worry about how the same tag is stored differently in different audio container formats.
332
+ `taglib-wasm` supports **automatic tag mapping** so you don’t have to worry about how the same tag is stored differently in different audio container formats.
266
333
 
267
334
  ### AcoustID example
268
335
 
@@ -477,7 +544,7 @@ interface TagLibConfig {
477
544
 
478
545
  ## 🌐 Runtime Compatibility
479
546
 
480
- TagLib WASM works seamlessly across all major JavaScript runtimes:
547
+ `taglib-wasm` works seamlessly across all major JavaScript runtimes:
481
548
 
482
549
  | Runtime | Status | Installation | Performance | TypeScript |
483
550
  | ----------- | ------- | --------------------------------- | ----------- | ---------- |
package/build/taglib.js CHANGED
@@ -4,22 +4,7 @@ var TagLibWASM = (() => {
4
4
  async function(moduleArg = {}) {
5
5
  var moduleRtn;
6
6
 
7
- var c=moduleArg,aa="object"==typeof window,ba="undefined"!=typeof WorkerGlobalScope,m="object"==typeof process&&process.versions?.node&&"renderer"!=process.type;"undefined"!=typeof __filename&&(_scriptName=__filename);var p="",q,r;
8
- if(m){var fs=require("fs");p=__dirname+"/";r=a=>{a=t(a)?new URL(a):a;return fs.readFileSync(a)};q=async a=>{a=t(a)?new URL(a):a;return fs.readFileSync(a,void 0)};process.argv.slice(2)}else if(aa||ba){try{p=(new URL(".",_scriptName)).href}catch{}q=async a=>{a=await fetch(a,{credentials:"same-origin"});if(a.ok)return a.arrayBuffer();throw Error(a.status+" : "+a.url);}}console.log.bind(console);var u=console.error.bind(console),v,w=!1,t=a=>a.startsWith("file://"),x,y,z,A,B,C,D,E,F,G,H,I=!1;
9
- function J(){var a=z.buffer;A=new Int8Array(a);C=new Int16Array(a);B=new Uint8Array(a);new Uint16Array(a);D=new Int32Array(a);E=new Uint32Array(a);F=new Float32Array(a);G=new Float64Array(a);H=new BigInt64Array(a);new BigUint64Array(a)}var K=0,L=null;function M(a){c.onAbort?.(a);a="Aborted("+a+")";u(a);w=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");y?.(a);throw a;}var N;
10
- async function ca(a){if(!v)try{var d=await q(a);return new Uint8Array(d)}catch{}if(a==N&&v)a=new Uint8Array(v);else if(r)a=r(a);else throw"both async and sync fetching of the wasm failed";return a}async function da(a,d){try{var b=await ca(a);return await WebAssembly.instantiate(b,d)}catch(e){u(`failed to asynchronously prepare wasm: ${e}`),M(e)}}
11
- async function ea(a){var d=N;if(!v&&"function"==typeof WebAssembly.instantiateStreaming&&!m)try{var b=fetch(d,{credentials:"same-origin"});return await WebAssembly.instantiateStreaming(b,a)}catch(e){u(`wasm streaming compile failed: ${e}`),u("falling back to ArrayBuffer instantiation")}return da(d,a)}var O=a=>{for(;0<a.length;)a.shift()(c)},P=[],Q=[],fa=()=>{var a=c.preRun.shift();Q.push(a)};class ha{constructor(a){this.J=a-24}}
12
- var R=0,ia=0,S=a=>{for(var d=0,b=0;b<a.length;++b){var e=a.charCodeAt(b);127>=e?d++:2047>=e?d+=2:55296<=e&&57343>=e?(d+=4,++b):d+=3}return d},T=(a,d,b,e)=>{if(!(0<e))return 0;var f=b;e=b+e-1;for(var h=0;h<a.length;++h){var g=a.codePointAt(h);if(127>=g){if(b>=e)break;d[b++]=g}else if(2047>=g){if(b+1>=e)break;d[b++]=192|g>>6;d[b++]=128|g&63}else if(65535>=g){if(b+2>=e)break;d[b++]=224|g>>12;d[b++]=128|g>>6&63;d[b++]=128|g&63}else{if(b+3>=e)break;d[b++]=240|g>>18;d[b++]=128|g>>12&63;d[b++]=128|g>>6&
13
- 63;d[b++]=128|g&63;h++}}d[b]=0;return b-f},U="undefined"!=typeof TextDecoder?new TextDecoder:void 0,ja=(a=0)=>{for(var d=B,b=a+NaN,e=a;d[e]&&!(e>=b);)++e;if(16<e-a&&d.buffer&&U)return U.decode(d.subarray(a,e));for(b="";a<e;){var f=d[a++];if(f&128){var h=d[a++]&63;if(192==(f&224))b+=String.fromCharCode((f&31)<<6|h);else{var g=d[a++]&63;f=224==(f&240)?(f&15)<<12|h<<6|g:(f&7)<<18|h<<12|g<<6|d[a++]&63;65536>f?b+=String.fromCharCode(f):(f-=65536,b+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else b+=
14
- String.fromCharCode(f)}return b},ma=(a,d,b,e)=>{var f={string:k=>{var l=0;if(null!==k&&void 0!==k&&0!==k){l=S(k)+1;var Y=V(l);T(k,B,Y,l);l=Y}return l},array:k=>{var l=V(k.length);A.set(k,l);return l}};a=c["_"+a];var h=[],g=0;if(e)for(var n=0;n<e.length;n++){var Z=f[b[n]];Z?(0===g&&(g=ka()),h[n]=Z(e[n])):h[n]=e[n]}b=a(...h);return b=function(k){0!==g&&la(g);return"string"===d?k?ja(k):"":"boolean"===d?!!k:k}(b)};c.printErr&&(u=c.printErr);c.wasmBinary&&(v=c.wasmBinary);c.ccall=ma;
15
- c.cwrap=(a,d,b,e)=>{var f=!b||b.every(h=>"number"===h||"boolean"===h);return"string"!==d&&f&&!e?c["_"+a]:(...h)=>ma(a,d,b,h,e)};c.setValue=function(a,d,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":A[a]=d;break;case "i8":A[a]=d;break;case "i16":C[a>>1]=d;break;case "i32":D[a>>2]=d;break;case "i64":H[a>>3]=BigInt(d);break;case "float":F[a>>2]=d;break;case "double":G[a>>3]=d;break;case "*":E[a>>2]=d;break;default:M(`invalid type for setValue: ${b}`)}};
16
- c.getValue=function(a,d="i8"){d.endsWith("*")&&(d="*");switch(d){case "i1":return A[a];case "i8":return A[a];case "i16":return C[a>>1];case "i32":return D[a>>2];case "i64":return H[a>>3];case "float":return F[a>>2];case "double":return G[a>>3];case "*":return E[a>>2];default:M(`invalid type for getValue: ${d}`)}};c.intArrayFromString=(a,d,b)=>{b=Array(0<b?b:S(a)+1);a=T(a,b,0,b.length);d&&(b.length=a);return b};c.ALLOC_NORMAL=0;
17
- c.allocate=(a,d)=>{d=1==d?V(a.length):na(a.length);a.subarray||a.slice||(a=new Uint8Array(a));B.set(a,d);return d};
18
- var na,la,V,ka,oa={a:(a,d,b)=>{var e=new ha(a);E[e.J+16>>2]=0;E[e.J+4>>2]=d;E[e.J+8>>2]=b;R=a;ia++;throw R;},b:()=>M(""),c:a=>{var d=B.length;a>>>=0;if(268435456<a)return!1;for(var b=1;4>=b;b*=2){var e=d*(1+.2/b);e=Math.min(e,a+100663296);a:{e=(Math.min(268435456,65536*Math.ceil(Math.max(a,e)/65536))-z.buffer.byteLength+65535)/65536|0;try{z.grow(e);J();var f=1;break a}catch(h){}f=void 0}if(f)return!0}return!1}},W=await (async function(){function a(b){W=b.exports;z=W.d;J();b=W;c._taglib_file_new_from_buffer=
19
- b.f;c._taglib_file_delete=b.g;c._taglib_file_save=b.h;c._taglib_file_is_valid=b.i;c._taglib_file_format=b.j;c._taglib_file_tag=b.k;c._taglib_tag_title=b.l;c._taglib_tag_artist=b.m;c._taglib_tag_album=b.n;c._taglib_tag_comment=b.o;c._taglib_tag_genre=b.p;c._taglib_tag_year=b.q;c._taglib_tag_track=b.r;c._taglib_tag_set_title=b.s;c._taglib_tag_set_artist=b.t;c._taglib_tag_set_album=b.u;c._taglib_tag_set_comment=b.v;c._taglib_tag_set_genre=b.w;c._taglib_tag_set_year=b.x;c._taglib_tag_set_track=b.y;c._taglib_file_audioproperties=
20
- b.z;c._taglib_audioproperties_length=b.A;c._taglib_audioproperties_bitrate=b.B;c._taglib_audioproperties_samplerate=b.C;c._taglib_audioproperties_channels=b.D;c._malloc=na=b.E;c._free=b.F;la=b.G;V=b.H;ka=b.I;K--;c.monitorRunDependencies?.(K);0==K&&L&&(b=L,L=null,b());return W}K++;c.monitorRunDependencies?.(K);var d={a:oa};if(c.instantiateWasm)return new Promise(b=>{c.instantiateWasm(d,(e,f)=>{b(a(e,f))})});N??=c.locateFile?c.locateFile("taglib.wasm",p):p+"taglib.wasm";return a((await ea(d)).instance)}());
21
- function X(){function a(){c.calledRun=!0;if(!w){I=!0;W.e();x?.(c);c.onRuntimeInitialized?.();if(c.postRun)for("function"==typeof c.postRun&&(c.postRun=[c.postRun]);c.postRun.length;){var d=c.postRun.shift();P.push(d)}O(P)}}if(0<K)L=X;else{if(c.preRun)for("function"==typeof c.preRun&&(c.preRun=[c.preRun]);c.preRun.length;)fa();O(Q);0<K?L=X:c.setStatus?(c.setStatus("Running..."),setTimeout(()=>{setTimeout(()=>c.setStatus(""),1);a()},1)):a()}}
22
- if(c.preInit)for("function"==typeof c.preInit&&(c.preInit=[c.preInit]);0<c.preInit.length;)c.preInit.shift()();X();I?moduleRtn=c:moduleRtn=new Promise((a,d)=>{x=a;y=d});
7
+ var Module=moduleArg;var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof WorkerGlobalScope!="undefined";var ENVIRONMENT_IS_NODE=typeof process=="object"&&process.versions?.node&&process.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else{}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var wasmMemory;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["h"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("taglib.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["g"];updateMemoryViews();assignWasmExports(wasmExports);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{resolve(receiveInstance(mod,inst))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var __abort_js=()=>abort("");var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset<winterOffset){stringToUTF8(winterName,std_name,17);stringToUTF8(summerName,dst_name,17)}else{stringToUTF8(winterName,dst_name,17);stringToUTF8(summerName,std_name,17)}};var getHeapMax=()=>268435456;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.language||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var ALLOC_STACK=1;var allocate=(slab,allocator)=>{var ret;if(allocator==ALLOC_STACK){ret=stackAlloc(slab.length)}else{ret=_malloc(slab.length)}if(!slab.subarray&&!slab.slice){slab=new Uint8Array(slab)}HEAPU8.set(slab,ret);return ret};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var ALLOC_NORMAL=0;{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"]}Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["intArrayFromString"]=intArrayFromString;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["allocate"]=allocate;var _taglib_file_new_from_buffer,_taglib_file_delete,_taglib_file_save,_taglib_file_is_valid,_taglib_file_format,_taglib_file_tag,_taglib_tag_title,_taglib_tag_artist,_taglib_tag_album,_taglib_tag_comment,_taglib_tag_genre,_taglib_tag_year,_taglib_tag_track,_taglib_tag_set_title,_taglib_tag_set_artist,_taglib_tag_set_album,_taglib_tag_set_comment,_taglib_tag_set_genre,_taglib_tag_set_year,_taglib_tag_set_track,_taglib_file_audioproperties,_taglib_audioproperties_length,_taglib_audioproperties_bitrate,_taglib_audioproperties_samplerate,_taglib_audioproperties_channels,_taglib_file_properties_json,_taglib_file_set_properties_json,_taglib_file_get_property,_taglib_file_set_property,_taglib_file_is_mp4,_taglib_mp4_get_item,_taglib_mp4_set_item,_taglib_mp4_remove_item,_malloc,_free,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current;function assignWasmExports(wasmExports){Module["_taglib_file_new_from_buffer"]=_taglib_file_new_from_buffer=wasmExports["i"];Module["_taglib_file_delete"]=_taglib_file_delete=wasmExports["j"];Module["_taglib_file_save"]=_taglib_file_save=wasmExports["k"];Module["_taglib_file_is_valid"]=_taglib_file_is_valid=wasmExports["l"];Module["_taglib_file_format"]=_taglib_file_format=wasmExports["m"];Module["_taglib_file_tag"]=_taglib_file_tag=wasmExports["n"];Module["_taglib_tag_title"]=_taglib_tag_title=wasmExports["o"];Module["_taglib_tag_artist"]=_taglib_tag_artist=wasmExports["p"];Module["_taglib_tag_album"]=_taglib_tag_album=wasmExports["q"];Module["_taglib_tag_comment"]=_taglib_tag_comment=wasmExports["r"];Module["_taglib_tag_genre"]=_taglib_tag_genre=wasmExports["s"];Module["_taglib_tag_year"]=_taglib_tag_year=wasmExports["t"];Module["_taglib_tag_track"]=_taglib_tag_track=wasmExports["u"];Module["_taglib_tag_set_title"]=_taglib_tag_set_title=wasmExports["v"];Module["_taglib_tag_set_artist"]=_taglib_tag_set_artist=wasmExports["w"];Module["_taglib_tag_set_album"]=_taglib_tag_set_album=wasmExports["x"];Module["_taglib_tag_set_comment"]=_taglib_tag_set_comment=wasmExports["y"];Module["_taglib_tag_set_genre"]=_taglib_tag_set_genre=wasmExports["z"];Module["_taglib_tag_set_year"]=_taglib_tag_set_year=wasmExports["A"];Module["_taglib_tag_set_track"]=_taglib_tag_set_track=wasmExports["B"];Module["_taglib_file_audioproperties"]=_taglib_file_audioproperties=wasmExports["C"];Module["_taglib_audioproperties_length"]=_taglib_audioproperties_length=wasmExports["D"];Module["_taglib_audioproperties_bitrate"]=_taglib_audioproperties_bitrate=wasmExports["E"];Module["_taglib_audioproperties_samplerate"]=_taglib_audioproperties_samplerate=wasmExports["F"];Module["_taglib_audioproperties_channels"]=_taglib_audioproperties_channels=wasmExports["G"];Module["_taglib_file_properties_json"]=_taglib_file_properties_json=wasmExports["H"];Module["_taglib_file_set_properties_json"]=_taglib_file_set_properties_json=wasmExports["I"];Module["_taglib_file_get_property"]=_taglib_file_get_property=wasmExports["J"];Module["_taglib_file_set_property"]=_taglib_file_set_property=wasmExports["K"];Module["_taglib_file_is_mp4"]=_taglib_file_is_mp4=wasmExports["L"];Module["_taglib_mp4_get_item"]=_taglib_mp4_get_item=wasmExports["M"];Module["_taglib_mp4_set_item"]=_taglib_mp4_set_item=wasmExports["N"];Module["_taglib_mp4_remove_item"]=_taglib_mp4_remove_item=wasmExports["O"];Module["_malloc"]=_malloc=wasmExports["P"];Module["_free"]=_free=wasmExports["Q"];__emscripten_stack_restore=wasmExports["R"];__emscripten_stack_alloc=wasmExports["S"];_emscripten_stack_get_current=wasmExports["T"]}var wasmImports={a:___cxa_throw,e:__abort_js,b:__tzset_js,f:_emscripten_resize_heap,c:_environ_get,d:_environ_sizes_get};var wasmExports=await createWasm();function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}function preInit(){if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}preInit();run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})}
23
8
 
24
9
 
25
10
  return moduleRtn;
package/build/taglib.wasm CHANGED
Binary file
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Main module exports for TagLib WASM
2
+ * @fileoverview Main module exports for taglib-wasm
3
3
  *
4
4
  * TagLib v2.1 compiled to WebAssembly with TypeScript bindings
5
5
  * for universal audio metadata handling.
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "taglib-wasm",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "TagLib compiled to WebAssembly with TypeScript bindings for universal audio metadata handling",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
7
7
  "exports": {
8
8
  ".": "./index.ts",
9
- "./workers": "./src/workers.ts"
9
+ "./workers": "./src/workers.ts",
10
+ "./simple": "./src/simple.ts"
10
11
  },
11
12
  "files": [
12
13
  "index.ts",
@@ -24,6 +25,7 @@
24
25
  "test:bun": "bun run tests/test-systematic.ts",
25
26
  "test:node": "node --loader ts-node/esm tests/test-systematic.ts",
26
27
  "update-taglib": "./scripts/update-taglib.sh",
28
+ "release": "./scripts/release.sh",
27
29
  "publish:npm": "echo 'Use GitHub Actions workflow for publishing'",
28
30
  "publish:github": "echo 'Use GitHub Actions workflow for publishing'",
29
31
  "publish:jsr": "echo 'Use GitHub Actions workflow for publishing'",
@@ -2,7 +2,7 @@
2
2
  * Enhanced API patterns inspired by node-taglib
3
3
  *
4
4
  * This file demonstrates potential API improvements that could be added
5
- * to TagLib WASM to improve developer experience.
5
+ * to taglib-wasm to improve developer experience.
6
6
  */
7
7
 
8
8
  import type { AudioFile, TagLibConfig } from "./types";
@@ -0,0 +1,201 @@
1
+ /**
2
+ * @fileoverview JSR-compatible simple API (uses taglib-jsr.ts instead of taglib.ts)
3
+ */
4
+
5
+ import { TagLib } from "./taglib-jsr.ts";
6
+ import type { AudioProperties, Tag } from "./types.ts";
7
+
8
+ // Cached TagLib instance for auto-initialization
9
+ let cachedTagLib: TagLib | null = null;
10
+
11
+ /**
12
+ * Get or create a TagLib instance with auto-initialization
13
+ */
14
+ async function getTagLib(): Promise<TagLib> {
15
+ if (!cachedTagLib) {
16
+ cachedTagLib = await TagLib.initialize({
17
+ debug: false,
18
+ memory: {
19
+ initial: 16 * 1024 * 1024, // 16MB default
20
+ maximum: 64 * 1024 * 1024, // 64MB max
21
+ },
22
+ });
23
+ }
24
+ return cachedTagLib;
25
+ }
26
+
27
+ /**
28
+ * Read a file's data from various sources
29
+ */
30
+ async function readFileData(file: string | Uint8Array | ArrayBuffer | File): Promise<Uint8Array> {
31
+ // Already a Uint8Array
32
+ if (file instanceof Uint8Array) {
33
+ return file;
34
+ }
35
+
36
+ // ArrayBuffer - convert to Uint8Array
37
+ if (file instanceof ArrayBuffer) {
38
+ return new Uint8Array(file);
39
+ }
40
+
41
+ // File object (browser)
42
+ if (typeof File !== 'undefined' && file instanceof File) {
43
+ return new Uint8Array(await file.arrayBuffer());
44
+ }
45
+
46
+ // String path - read from filesystem
47
+ if (typeof file === 'string') {
48
+ // Deno
49
+ if (typeof Deno !== 'undefined') {
50
+ return await Deno.readFile(file);
51
+ }
52
+
53
+ throw new Error('File path reading not supported in this environment');
54
+ }
55
+
56
+ throw new Error('Invalid file input type');
57
+ }
58
+
59
+ /**
60
+ * Read metadata tags from an audio file
61
+ */
62
+ export async function readTags(file: string | Uint8Array | ArrayBuffer | File): Promise<Tag> {
63
+ const taglib = await getTagLib();
64
+ const audioData = await readFileData(file);
65
+
66
+ const audioFile = taglib.openFile(audioData);
67
+ try {
68
+ if (!audioFile.isValid()) {
69
+ throw new Error('Invalid audio file');
70
+ }
71
+
72
+ return audioFile.tag();
73
+ } finally {
74
+ audioFile.dispose();
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Write metadata tags to an audio file
80
+ */
81
+ export async function writeTags(
82
+ file: string | Uint8Array | ArrayBuffer | File,
83
+ tags: Partial<Tag>,
84
+ options?: number
85
+ ): Promise<Uint8Array> {
86
+ const taglib = await getTagLib();
87
+ const audioData = await readFileData(file);
88
+
89
+ const audioFile = taglib.openFile(audioData);
90
+ try {
91
+ if (!audioFile.isValid()) {
92
+ throw new Error('Invalid audio file');
93
+ }
94
+
95
+ // Write each tag if defined
96
+ if (tags.title !== undefined) audioFile.setTitle(tags.title);
97
+ if (tags.artist !== undefined) audioFile.setArtist(tags.artist);
98
+ if (tags.album !== undefined) audioFile.setAlbum(tags.album);
99
+ if (tags.comment !== undefined) audioFile.setComment(tags.comment);
100
+ if (tags.genre !== undefined) audioFile.setGenre(tags.genre);
101
+ if (tags.year !== undefined) audioFile.setYear(tags.year);
102
+ if (tags.track !== undefined) audioFile.setTrack(tags.track);
103
+
104
+ // Save changes to in-memory buffer
105
+ if (!audioFile.save()) {
106
+ throw new Error('Failed to save changes');
107
+ }
108
+
109
+ return audioData;
110
+ } finally {
111
+ audioFile.dispose();
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Read audio properties from a file
117
+ */
118
+ export async function readProperties(file: string | Uint8Array | ArrayBuffer | File): Promise<AudioProperties> {
119
+ const taglib = await getTagLib();
120
+ const audioData = await readFileData(file);
121
+
122
+ const audioFile = taglib.openFile(audioData);
123
+ try {
124
+ if (!audioFile.isValid()) {
125
+ throw new Error('Invalid audio file');
126
+ }
127
+
128
+ return audioFile.audioProperties();
129
+ } finally {
130
+ audioFile.dispose();
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Tag field constants for go-taglib compatibility
136
+ */
137
+ export const Title = "title";
138
+ export const Artist = "artist";
139
+ export const Album = "album";
140
+ export const Comment = "comment";
141
+ export const Genre = "genre";
142
+ export const Year = "year";
143
+ export const Track = "track";
144
+ export const AlbumArtist = "albumartist";
145
+ export const Composer = "composer";
146
+ export const DiscNumber = "discnumber";
147
+
148
+ /**
149
+ * Check if a file is a valid audio file
150
+ */
151
+ export async function isValidAudioFile(file: string | Uint8Array | ArrayBuffer | File): Promise<boolean> {
152
+ try {
153
+ const taglib = await getTagLib();
154
+ const audioData = await readFileData(file);
155
+
156
+ const audioFile = taglib.openFile(audioData);
157
+ const valid = audioFile.isValid();
158
+ audioFile.dispose();
159
+
160
+ return valid;
161
+ } catch {
162
+ return false;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Get the audio format of a file
168
+ */
169
+ export async function getFormat(file: string | Uint8Array | ArrayBuffer | File): Promise<string | undefined> {
170
+ const taglib = await getTagLib();
171
+ const audioData = await readFileData(file);
172
+
173
+ const audioFile = taglib.openFile(audioData);
174
+ try {
175
+ if (!audioFile.isValid()) {
176
+ return undefined;
177
+ }
178
+
179
+ return audioFile.format();
180
+ } finally {
181
+ audioFile.dispose();
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Clear all tags from a file
187
+ */
188
+ export async function clearTags(file: string | Uint8Array | ArrayBuffer | File): Promise<Uint8Array> {
189
+ return writeTags(file, {
190
+ title: "",
191
+ artist: "",
192
+ album: "",
193
+ comment: "",
194
+ genre: "",
195
+ year: 0,
196
+ track: 0,
197
+ });
198
+ }
199
+
200
+ // Type exports for convenience
201
+ export type { Tag, AudioProperties } from "./types.ts";