@wasmedia/wexiftool 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/LICENSE-exiftool +19 -0
- package/README.md +105 -2
- package/dist/cli.cjs +19 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.es.js +81 -0
- package/dist/index-BUUKaFua.js +681 -0
- package/dist/index-Czh_jOH1.cjs +1 -0
- package/dist/node-6KJNNt7l.cjs +1 -0
- package/dist/node-WHSUCdKF.js +102 -0
- package/dist/wexiftool-CUKoJisL.js +761 -0
- package/dist/wexiftool-Dt9AnMsh.cjs +4 -0
- package/dist/wexiftool.cjs +1 -0
- package/dist/wexiftool.d.ts +255 -0
- package/dist/wexiftool.es.js +5 -0
- package/dist/worker.cjs +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.es.js +64 -0
- package/package.json +69 -4
- package/wasm/package.json +4 -0
- package/wasm/perl.js +2 -0
- package/wasm/perl.wasm +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wmedia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/LICENSE-exiftool
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
ExifTool License Notice
|
|
2
|
+
=======================
|
|
3
|
+
|
|
4
|
+
This package bundles ExifTool by Phil Harvey (https://exiftool.org/).
|
|
5
|
+
|
|
6
|
+
ExifTool is free software; you can redistribute it and/or modify it
|
|
7
|
+
under the same terms as Perl itself, which means either:
|
|
8
|
+
|
|
9
|
+
a) the GNU General Public License as published by the Free Software
|
|
10
|
+
Foundation; either version 1, or (at your option) any later version, or
|
|
11
|
+
|
|
12
|
+
b) the "Artistic License" which comes with Perl.
|
|
13
|
+
|
|
14
|
+
For the full text of these licenses, see:
|
|
15
|
+
- GNU GPL: https://www.gnu.org/licenses/gpl.html
|
|
16
|
+
- Artistic License: https://dev.perl.org/licenses/artistic.html
|
|
17
|
+
|
|
18
|
+
ExifTool homepage: https://exiftool.org/
|
|
19
|
+
ExifTool author: Phil Harvey (philharvey66 at gmail.com)
|
package/README.md
CHANGED
|
@@ -1,3 +1,106 @@
|
|
|
1
|
-
#
|
|
1
|
+
# wexiftool
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://gitlab.com/wasmedia/wexiftool/-/pipelines)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://wasmedia.gitlab.io/wexiftool/)
|
|
6
|
+
|
|
7
|
+
Run the real, full-featured [ExifTool](https://exiftool.org/) in the browser and Node.js — no server required.
|
|
8
|
+
|
|
9
|
+
wexiftool compiles Perl 5.40 to WebAssembly via Emscripten and bundles Phil Harvey's ExifTool into the WASM virtual filesystem. This gives you the complete ExifTool (read **and** write support for 400+ metadata formats) as a JavaScript/TypeScript library.
|
|
10
|
+
|
|
11
|
+
## CLI Usage
|
|
12
|
+
|
|
13
|
+
Use it just like native exiftool — no Perl installation required:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Install globally
|
|
17
|
+
npm install -g @wasmedia/wexiftool
|
|
18
|
+
|
|
19
|
+
# Or use directly with npx
|
|
20
|
+
npx @wasmedia/wexiftool photo.jpg
|
|
21
|
+
npx @wasmedia/wexiftool -Artist="Jane Doe" photo.jpg
|
|
22
|
+
npx @wasmedia/wexiftool -json -G *.jpg
|
|
23
|
+
npx @wasmedia/wexiftool -ver
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
All standard exiftool flags and options work as expected.
|
|
27
|
+
|
|
28
|
+
## API Usage
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { WExifTool } from '@wasmedia/wexiftool';
|
|
32
|
+
|
|
33
|
+
const wexiftool = await WExifTool.create();
|
|
34
|
+
|
|
35
|
+
// Read metadata
|
|
36
|
+
const metadata = await wexiftool.read(file); // File, Blob, ArrayBuffer, or Uint8Array
|
|
37
|
+
|
|
38
|
+
// Write metadata
|
|
39
|
+
const modified = await wexiftool.write(file, {
|
|
40
|
+
Artist: 'Jane Doe',
|
|
41
|
+
Copyright: '2026 Jane Doe',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Pass arbitrary exiftool flags
|
|
45
|
+
const raw = await wexiftool.readRaw(file, ['-G', '-s']);
|
|
46
|
+
|
|
47
|
+
// Run any exiftool command (low-level)
|
|
48
|
+
const result = await wexiftool.exec(['-json', '-G'], files);
|
|
49
|
+
|
|
50
|
+
wexiftool.destroy(); // Free WASM memory
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install @wasmedia/wexiftool
|
|
57
|
+
# or
|
|
58
|
+
pnpm add @wasmedia/wexiftool
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A standalone UMD bundle with inlined WASM is also available for `<script>` tag usage at `https://exiftool.wasmedia.org/wexiftool.standalone.js` (see [Standalone Bundle](docs/standalone-bundle.md)). It's ~70MB and not part of the npm package; the npm package, which loads the WASM separately, is recommended for production.
|
|
62
|
+
|
|
63
|
+
## Demo
|
|
64
|
+
|
|
65
|
+
A playground demo is available where you can drop an image to read and edit its metadata entirely client-side. Deployed via GitLab Pages.
|
|
66
|
+
|
|
67
|
+
## How It Works
|
|
68
|
+
|
|
69
|
+
1. Perl 5.40 is cross-compiled to WASM using Emscripten inside a Docker container
|
|
70
|
+
2. ExifTool's Perl modules are embedded into the Emscripten virtual filesystem (MEMFS)
|
|
71
|
+
3. Image data is written into MEMFS, ExifTool processes it via `callMain()`, results are extracted
|
|
72
|
+
4. A TypeScript API wraps the low-level Emscripten interface with a clean async interface
|
|
73
|
+
|
|
74
|
+
## Building from Source
|
|
75
|
+
|
|
76
|
+
Requires Docker.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pnpm install
|
|
80
|
+
pnpm run build:wasm # Builds Perl + ExifTool WASM (Docker, ~10-15 min first time)
|
|
81
|
+
pnpm run build # Builds the JS/TS library
|
|
82
|
+
pnpm run dev # Launches the demo playground dev server
|
|
83
|
+
pnpm test # Runs tests
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Size
|
|
87
|
+
|
|
88
|
+
The WASM payload is approximately 53MB uncompressed (~13MB gzipped). The library lazy-loads the WASM on first use.
|
|
89
|
+
|
|
90
|
+
## Status
|
|
91
|
+
|
|
92
|
+
End-to-end functional: `exiftool -ver` returns the version, EXIF metadata reads from a real JPEG fixture, and EXIF tag writes round-trip through `WExifTool.write()` → `WExifTool.read()`. The currently statically-linked XS set is `File::Glob` (more in `build/build-xs-extensions.sh`); ExifTool's read and most-of-write paths are pure Perl and work with what `libperl.a` already provides.
|
|
93
|
+
|
|
94
|
+
Build is single-threaded by design (`usethreads=undef`, `usemultiplicity=undef`) and uses no dynamic loading (`usedl=undef`).
|
|
95
|
+
|
|
96
|
+
**Known issue:** writing `EXIF:Copyright` (tag ID `0x8298`) silently corrupts the IFD entry — verified that the resulting file is unreadable by both wexiftool and native exiftool. All other EXIF tags tested round-trip correctly. Suspected signed-vs-unsigned 16-bit arithmetic on the tag ID inside ExifTool's WASM build. See `CHANGELOG.md` Known Issues.
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup, project layout, and PR guidelines. Notable changes are tracked in [CHANGELOG.md](CHANGELOG.md).
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
105
|
+
|
|
106
|
+
This project bundles [ExifTool](https://exiftool.org/) by Phil Harvey, which is licensed under the Perl Artistic License / GPL. See [LICENSE-exiftool](LICENSE-exiftool) for details.
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";const f=require("fs"),u=require("path"),p=require("./wexiftool-Dt9AnMsh.cjs");async function d(){const t=process.argv.slice(2);t.length===0&&(w(),process.exit(1));const{flags:o,filePaths:i}=x(t);if(o.includes("-ver")||o.includes("-version")){const e=await p.WExifTool.create(),n=await e.exec([...o]);process.stdout.write(n.stdout+`
|
|
3
|
+
`),n.stderr&&process.stderr.write(n.stderr+`
|
|
4
|
+
`),e.destroy(),process.exit(n.exitCode)}i.length===0&&(console.error("Error: No input files specified."),process.exit(1));for(const e of i){const n=u.resolve(e);f.existsSync(n)||(console.error(`Error: File not found: ${e}`),process.exit(1)),f.statSync(n).isFile()||(console.error(`Error: Not a file: ${e}`),process.exit(1))}const c=o.some(e=>e.includes("=")&&e.startsWith("-")&&!e.startsWith("-if")),s=await p.WExifTool.create(),r=new Map(i.map(e=>[e,e])),a=[...o,...i];c&&!o.includes("-overwrite_original")&&!o.includes("-overwrite_original_in_place")&&a.unshift("-overwrite_original");const l=await s.exec(a,r,{writeBack:c});l.stdout&&process.stdout.write(l.stdout+`
|
|
5
|
+
`),l.stderr&&process.stderr.write(l.stderr+`
|
|
6
|
+
`),s.destroy(),process.exit(l.exitCode)}function x(t){const o=[],i=[],c=new Set(["-tagsFromFile","-tagsfromfile","-o","-out","-w","-textOut","-textout","-srcfile","-sep","-d","-dateformat","-dateFormat","-charset","-config"]);let s=0;for(;s<t.length;){const r=t[s];if(r==="--"){i.push(...t.slice(s+1));break}if(r.startsWith("-")){o.push(r);const a=r.toLowerCase().split("=")[0];c.has(a)&&!r.includes("=")&&s+1<t.length&&(s++,o.push(t[s]))}else i.push(r);s++}return{flags:o,filePaths:i}}function w(){console.log(`wexiftool - ExifTool running on WebAssembly
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
wexiftool [OPTIONS] FILE...
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
wexiftool photo.jpg Read all metadata
|
|
13
|
+
wexiftool -json photo.jpg Output as JSON
|
|
14
|
+
wexiftool -Artist="Jane Doe" photo.jpg Write a tag
|
|
15
|
+
wexiftool -G -s photo.jpg Grouped, short output
|
|
16
|
+
wexiftool -ver Show ExifTool version
|
|
17
|
+
|
|
18
|
+
All standard ExifTool options are supported.
|
|
19
|
+
See https://exiftool.org/ for full documentation.`)}d().catch(t=>{console.error(t.message||t),process.exit(1)});
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { }
|
package/dist/cli.es.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync as p, statSync as u } from "fs";
|
|
3
|
+
import { resolve as d } from "path";
|
|
4
|
+
import { W as f } from "./wexiftool-CUKoJisL.js";
|
|
5
|
+
async function x() {
|
|
6
|
+
const t = process.argv.slice(2);
|
|
7
|
+
t.length === 0 && (h(), process.exit(1));
|
|
8
|
+
const { flags: o, filePaths: i } = w(t);
|
|
9
|
+
if (o.includes("-ver") || o.includes("-version")) {
|
|
10
|
+
const e = await f.create(), n = await e.exec([...o]);
|
|
11
|
+
process.stdout.write(n.stdout + `
|
|
12
|
+
`), n.stderr && process.stderr.write(n.stderr + `
|
|
13
|
+
`), e.destroy(), process.exit(n.exitCode);
|
|
14
|
+
}
|
|
15
|
+
i.length === 0 && (console.error("Error: No input files specified."), process.exit(1));
|
|
16
|
+
for (const e of i) {
|
|
17
|
+
const n = d(e);
|
|
18
|
+
p(n) || (console.error(`Error: File not found: ${e}`), process.exit(1)), u(n).isFile() || (console.error(`Error: Not a file: ${e}`), process.exit(1));
|
|
19
|
+
}
|
|
20
|
+
const a = o.some(
|
|
21
|
+
(e) => e.includes("=") && e.startsWith("-") && !e.startsWith("-if")
|
|
22
|
+
), s = await f.create(), r = new Map(i.map((e) => [e, e])), c = [...o, ...i];
|
|
23
|
+
a && !o.includes("-overwrite_original") && !o.includes("-overwrite_original_in_place") && c.unshift("-overwrite_original");
|
|
24
|
+
const l = await s.exec(c, r, { writeBack: a });
|
|
25
|
+
l.stdout && process.stdout.write(l.stdout + `
|
|
26
|
+
`), l.stderr && process.stderr.write(l.stderr + `
|
|
27
|
+
`), s.destroy(), process.exit(l.exitCode);
|
|
28
|
+
}
|
|
29
|
+
function w(t) {
|
|
30
|
+
const o = [], i = [], a = /* @__PURE__ */ new Set([
|
|
31
|
+
"-tagsFromFile",
|
|
32
|
+
"-tagsfromfile",
|
|
33
|
+
"-o",
|
|
34
|
+
"-out",
|
|
35
|
+
"-w",
|
|
36
|
+
"-textOut",
|
|
37
|
+
"-textout",
|
|
38
|
+
"-srcfile",
|
|
39
|
+
"-sep",
|
|
40
|
+
"-d",
|
|
41
|
+
"-dateformat",
|
|
42
|
+
"-dateFormat",
|
|
43
|
+
"-charset",
|
|
44
|
+
"-config"
|
|
45
|
+
]);
|
|
46
|
+
let s = 0;
|
|
47
|
+
for (; s < t.length; ) {
|
|
48
|
+
const r = t[s];
|
|
49
|
+
if (r === "--") {
|
|
50
|
+
i.push(...t.slice(s + 1));
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
if (r.startsWith("-")) {
|
|
54
|
+
o.push(r);
|
|
55
|
+
const c = r.toLowerCase().split("=")[0];
|
|
56
|
+
a.has(c) && !r.includes("=") && s + 1 < t.length && (s++, o.push(t[s]));
|
|
57
|
+
} else
|
|
58
|
+
i.push(r);
|
|
59
|
+
s++;
|
|
60
|
+
}
|
|
61
|
+
return { flags: o, filePaths: i };
|
|
62
|
+
}
|
|
63
|
+
function h() {
|
|
64
|
+
console.log(`wexiftool - ExifTool running on WebAssembly
|
|
65
|
+
|
|
66
|
+
Usage:
|
|
67
|
+
wexiftool [OPTIONS] FILE...
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
wexiftool photo.jpg Read all metadata
|
|
71
|
+
wexiftool -json photo.jpg Output as JSON
|
|
72
|
+
wexiftool -Artist="Jane Doe" photo.jpg Write a tag
|
|
73
|
+
wexiftool -G -s photo.jpg Grouped, short output
|
|
74
|
+
wexiftool -ver Show ExifTool version
|
|
75
|
+
|
|
76
|
+
All standard ExifTool options are supported.
|
|
77
|
+
See https://exiftool.org/ for full documentation.`);
|
|
78
|
+
}
|
|
79
|
+
x().catch((t) => {
|
|
80
|
+
console.error(t.message || t), process.exit(1);
|
|
81
|
+
});
|