exiftool-vendored 30.1.0 → 30.3.0

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,543 +5,250 @@
5
5
  [![npm version](https://img.shields.io/npm/v/exiftool-vendored.svg)](https://www.npmjs.com/package/exiftool-vendored)
6
6
  [![Node.js CI](https://github.com/photostructure/exiftool-vendored.js/actions/workflows/node.js.yml/badge.svg)](https://github.com/photostructure/exiftool-vendored.js/actions/workflows/node.js.yml)
7
7
  [![GitHub issues](https://img.shields.io/github/issues/photostructure/exiftool-vendored.js.svg)](https://github.com/photostructure/exiftool-vendored.js/issues)
8
- [![Known Vulnerabilities](https://snyk.io/test/github/photostructure/exiftool-vendored.js/badge.svg?targetFile=package.json)](https://snyk.io/test/github/photostructure/exiftool-vendored.js?targetFile=package.json)
9
8
 
10
- ## Features
9
+ ## Installation & Quick Start
11
10
 
12
- 1. **Best-of-class cross-platform performance and reliability**.
11
+ **Requirements**: Node.js Active LTS or Maintenance LTS versions only
13
12
 
14
- This library enables [PhotoStructure](https://photostructure.com) and [over 1,000](https://github.com/photostructure/exiftool-vendored.js/network/dependents?package_id=UGFja2FnZS0xNjYxNjY2MQ%3D%3D) other projects to read and write metadata in photos and videos.
15
-
16
- Expect [an order of magnitude faster performance](#performance) than other Node.js ExifTool modules.
17
-
18
- Thanks to being based on [ExifTool](https://exiftool.org/), it's the state of the art in high quality metadata extraction for thousands of file types.
19
-
20
- 1. Best-effort extraction of
21
-
22
- - **dates** with [correct timezone offset encoding](#dates)
23
- - **latitudes & longitudes** as floats (where negative values indicate west or south of the meridian)
24
-
25
- 1. Support for
26
-
27
- - [reading tags](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#read)
28
- - extracting embedded binaries, like [thumbnail](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#extractThumbnail) and [preview](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#extractPreview) images
29
- - [writing tags](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#write)
30
- - [rescuing metadata](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#rewriteAllTags)
31
-
32
- 1. **[Robust type definitions](#tags)** of the top 99.5% tags used by over 6,000
33
- different camera makes and models (see an [example](https://photostructure.github.io/exiftool-vendored.js/interfaces/EXIFTags.html#CreateDate))
34
-
35
- 1. **Automated updates** to ExifTool ([as new versions come out
36
- frequently](https://exiftool.org/history.html))
37
-
38
- 1. **Robust test coverage**, performed with on [macOS, Linux, and
39
- Windows](https://github.com/photostructure/exiftool-vendored.js/actions?query=workflow%3A%22Node.js+CI%22)
40
-
41
- ## Installation
42
-
43
- ```sh
44
- npm install --save exiftool-vendored
13
+ ```bash
14
+ npm install exiftool-vendored
45
15
  ```
46
16
 
47
- ### Debug logging
48
-
49
- If anything doesn't work, the first thing to try is enabling the logger.
50
-
51
- You can provide a [Logger implementation](https://photostructure.github.io/batch-cluster.js/interfaces/Logger.html) via [`ExifToolOptions.logger`](https://photostructure.github.io/exiftool-vendored.js/interfaces/ExifToolOptions.html#logger), or set the environment variable `NODE_DEBUG=exiftool-vendored`. [See the debuglog() documentation](https://nodejs.org/docs/latest/api/util.html#utildebuglogsection-callback) for more details.
52
-
53
- ### Regarding use within Electron
54
-
55
- Due to how different every Electron application setup is, and how new versions
56
- frequently have breaking changes, **do not ask for help by opening a github
57
- issue on this project.**
58
-
59
- Please seek help via StackOverflow, the Electron discord, or other channels.
60
-
61
- ### Electron-builder support
62
-
63
- Add the following pattern to `electron-builder.yml`'s `asarUnpack`:
64
-
65
- ```yaml
66
- - "node_modules/exiftool-vendored.*/**/*"
67
- ```
68
-
69
- The default `exiftoolPath` implementation will detect `app.asar` in your `require`
70
- path and replace it with `app.asar.unpacked` automatically.
71
-
72
- ### Electron-forge support
73
-
74
- Version 25.0 of this library added experimental support for `electron-forge`:
75
- add the following element to your `ForgeConfig.packagerConfig.extraResource`
76
- string array, and things should "just work" **for the main process**.
77
-
78
- ```ts
79
- "./node_modules/exiftool-vendored." +
80
- (process.platform === "win32" ? "exe" : "pl");
81
- ```
82
-
83
- **If your main process forks any node subprocesses, `process.resourcesPath` _will
84
- not be set_ in those subprocesses, and the default `exiftoolPath` won't work.**
85
-
86
- If this is your case, you must provide a correct implementation of
87
- [ExifToolOptions.exiftoolPath](https://photostructure.github.io/exiftool-vendored.js/interfaces/ExifToolOptions.html#exiftoolPath),
88
- either by passing through `resourcesPath` via `process.env`, or some other
89
- method.
90
-
91
- ### Installation notes
92
-
93
- - `exiftool-vendored` provides an installation of ExifTool relevant for your
94
- local platform through
95
- [optionalDependencies](https://docs.npmjs.com/files/package.json#optionaldependencies).
96
-
97
- - You shouldn't include either [exiftool-vendored.exe](https://github.com/photostructure/exiftool-vendored.exe) or
98
- [exiftool-vendored.pl](https://github.com/photostructure/exiftool-vendored.pl) as direct dependencies to your project, unless you know
99
- what you're doing.
100
-
101
- - If you're installing on a minimal Linux distribution, you may need to install `perl`. On Alpine, run `apk add perl`.
102
-
103
- - Node.js's `-slim` docker images don't include a working `perl` build. Use the non-slim image instead. [See the issue report for details.](https://github.com/photostructure/exiftool-vendored.js/issues/168)
104
-
105
- - If the platform-correct vendor module (`exiftool-vendored.exe` or `exiftool-vendored.pl`) is not found, `exiftool` is searched for on your `PATH`. Note that _very_ old versions of `exiftool` are found on currently-supported Linux distributions which this library will not work correctly with.
106
-
107
- ## Upgrading
108
-
109
- See the
110
- [CHANGELOG](https://github.com/photostructure/exiftool-vendored.js/blob/main/CHANGELOG.md)
111
- for breaking changes since you last updated.
112
-
113
- ### Major version bumps
114
-
115
- I bump the major version if there's a **chance** existing code might be
116
- affected.
117
-
118
- I've been bit too many times by my code breaking when I pull in minor or patch
119
- upgrades with other libraries. I think it's better to be pessimistic in code
120
- change impact analysis: "over-promise and under-deliver" your breaking-code
121
- changes.
122
-
123
- When you upgrade to a new major version, please take a bit more care in
124
- validating your own systems, but don't be surprised when everything still works.
125
-
126
- ## Usage
127
-
128
- There are many configuration options to ExifTool, but all values have (more or
129
- less sensible) defaults.
130
-
131
- Those defaults have been used to create the
132
- [`exiftool`](https://photostructure.github.io/exiftool-vendored.js/modules.html#exiftool) singleton.
133
- Note that if you _don't_ use the default singleton, you don't need to `.end()`
134
- it.
135
-
136
- ```js
137
- // We're using the singleton here for convenience:
138
- const exiftool = require("exiftool-vendored").exiftool;
139
-
140
- // And to verify everything is working:
141
- exiftool
142
- .version()
143
- .then((version) => console.log(`We're running ExifTool v${version}`));
144
- ```
145
-
146
- If the default [ExifTool constructor
147
- parameters](https://photostructure.github.io/exiftool-vendored.js/interfaces/ExifToolOptions.html)
148
- wont' work for you, it's just a class that takes an options hash:
149
-
150
- ```js
151
- const ExifTool = require("exiftool-vendored").ExifTool;
152
- const exiftool = new ExifTool({ taskTimeoutMillis: 5000 });
153
- ```
154
-
155
- You should only use the exported default `exiftool` singleton, or only create one instance of `ExifTool` as a singleton.
156
-
157
- Remember to `.end()` whichever singleton you use.
158
-
159
- ### General API
160
-
161
- `ExifTool.read()` returns a Promise to a [Tags](https://photostructure.github.io/exiftool-vendored.js/interfaces/Tags.html) instance. Note
162
- that errors may be returned either by rejecting the promise, or for less
163
- severe problems, via the `errors` field.
164
-
165
- All other public ExifTool methods return `Promise<void>`, and will reject
166
- the promise if the operation is not successful.
17
+ ```javascript
18
+ import { exiftool } from "exiftool-vendored";
167
19
 
168
- ### `Tags` types
20
+ // Read metadata
21
+ const tags = await exiftool.read("photo.jpg");
22
+ console.log(`Camera: ${tags.Make} ${tags.Model}`);
23
+ console.log(`Taken: ${tags.DateTimeOriginal}`);
24
+ console.log(`Size: ${tags.ImageWidth}x${tags.ImageHeight}`);
169
25
 
170
- ExifTool knows how to extract _several thousand_ different tag fields.
171
-
172
- Unfortunately, TypeScript crashes with `error TS2590: Expression produces a union type that is too complex to represent` if the `Tags` interface was comprehensive.
173
-
174
- Instead, we build a corpus of "commonly seen" tags from over 10,000 different
175
- digital camera makes and models, many from the [ExifTool metadata
176
- repository](https://exiftool.org/sample_images.html) and <raw.pixls.us>.
177
-
178
- Here are some example fields:
179
-
180
- ```ts
181
- /** ★☆☆☆ ✔ Example: 200 */
182
- ISO?: number
183
-
184
- /** ★★★★ ✔ Example: 1920 */
185
- ImageHeight?: number
186
-
187
- /** ★★★★ ✔ Example: 1080 */
188
- ImageWidth?: number
189
-
190
- /** ★★★★ ✔ Example: "image/jpeg" */
191
- MIMEType?: string
192
- ```
193
-
194
- The stars represent how common that field has a value in the example corpus. ★★★★ fields are found in > 50% of the examples.
195
- ☆☆☆☆ fields are found in < 1% of examples.
196
-
197
- The checkmark denotes if the field is found in "popular" cameras (like recent
198
- Nikon, Canon, Sony, and Apple devices).
199
-
200
- ### Caveats with `Tags`
201
-
202
- **The fields in `Tags` are not comprehensive.**
203
-
204
- Just because a field is missing from the Tags interface **does not mean the
205
- field doesn't exist in the returned object**. This library doesn't exclude
206
- unknown fields, in other words. It's up to you and your code to look for other
207
- fields you expect and cast to a more relevant interface.
208
-
209
- ### Logging and events
210
-
211
- To enable trace, debug, info, warning, or error logging from this library and
212
- the underlying `batch-cluster` library, provide a [Logger](https://photostructure.github.io/batch-cluster.js/interfaces/Logger.html) instance to the `ExifTool` constructor options.
213
-
214
- ExifTool instances emits [many lifecycle and error events](https://photostructure.github.io/batch-cluster.js/interfaces/BatchClusterEvents.html#beforeEnd) via `batch-cluster`.
215
-
216
- ### Reading tags
217
-
218
- ```js
219
- exiftool
220
- .read("path/to/image.jpg")
221
- .then((tags /*: Tags */) =>
222
- console.log(
223
- `Make: ${tags.Make}, Model: ${tags.Model}, Errors: ${tags.errors}`,
224
- ),
225
- )
226
- .catch((err) => console.error("Something terrible happened: ", err));
227
- ```
228
-
229
- ### Extracting embedded images
26
+ // Write metadata
27
+ await exiftool.write("photo.jpg", {
28
+ XPComment: "Amazing sunset!",
29
+ Copyright: "© 2024 Your Name",
30
+ });
230
31
 
231
- Extract the low-resolution thumbnail in `path/to/image.jpg`, write it to
232
- `path/to/thumbnail.jpg`, and return a `Promise<void>` that is fulfilled
233
- when the image is extracted:
32
+ // Extract thumbnail
33
+ await exiftool.extractThumbnail("photo.jpg", "thumb.jpg");
234
34
 
235
- ```js
236
- exiftool.extractThumbnail("path/to/image.jpg", "path/to/thumbnail.jpg");
35
+ // The singleton instance automatically cleans up on process exit by default.
36
+ // If you need immediate cleanup or have disabled registerExitHandlers:
37
+ // await exiftool.end();
237
38
  ```
238
39
 
239
- Extract the `Preview` image (only found in some images):
40
+ ## Why exiftool-vendored?
240
41
 
241
- ```js
242
- exiftool.extractPreview("path/to/image.jpg", "path/to/preview.jpg");
243
- ```
42
+ ### ⚡ **Performance**
244
43
 
245
- Extract the `JpgFromRaw` image (found in some RAW images):
44
+ Order of magnitude faster than other Node.js ExifTool modules. Powers [PhotoStructure](https://photostructure.com) and [1,000+ other projects](https://github.com/photostructure/exiftool-vendored.js/network/dependents?package_id=UGFja2FnZS0xNjYxNjY2MQ%3D%3D).
246
45
 
247
- ```js
248
- exiftool.extractJpgFromRaw("path/to/image.cr2", "path/to/fromRaw.jpg");
249
- ```
46
+ ### 🔧 **Robust**
250
47
 
251
- Extract the binary value from "tagname" tag in `path/to/image.jpg`
252
- and write it to `dest.bin` (which cannot exist already
253
- and whose parent directory must already exist):
48
+ - **Cross-platform**: macOS, Linux, Windows
49
+ - **Comprehensive**: Read, write, extract embedded images
50
+ - **Reliable**: Battle-tested with extensive test coverage
254
51
 
255
- ```js
256
- exiftool.extractBinaryTag("tagname", "path/to/file.exf", "path/to/dest.bin");
257
- ```
52
+ ### 📚 **Developer-Friendly**
258
53
 
259
- ### Writing tags
54
+ - **TypeScript**: Full type definitions for thousands of metadata fields
55
+ - **Smart dates**: Timezone-aware `ExifDateTime` classes
56
+ - **Auto-generated tags**: Based on 6,000+ real camera samples
260
57
 
261
- Note that only a portion of tags is writable. Refer to [the
262
- documentation](https://exiftool.org/TagNames/index.html)
263
- and look under the "Writable" column.
58
+ ## Core Features
264
59
 
265
- If you apply malformed values or ask to write to tags that aren't
266
- supported, the returned `Promise` will be rejected.
60
+ ### Reading Metadata
267
61
 
268
- Only string and numeric primitives are supported as values to the object.
62
+ ```javascript
63
+ const tags = await exiftool.read("photo.jpg");
269
64
 
270
- To write a comment to the given file so it shows up in the Windows Explorer
271
- Properties panel:
65
+ // Camera info
66
+ console.log(tags.Make, tags.Model, tags.LensModel);
272
67
 
273
- ```js
274
- exiftool.write("path/to/file.jpg", { XPComment: "this is a test comment" });
275
- ```
68
+ // Capture settings
69
+ console.log(tags.ISO, tags.FNumber, tags.ExposureTime);
276
70
 
277
- To change the DateTimeOriginal, CreateDate and ModifyDate tags (using the
278
- [AllDates](https://exiftool.org/TagNames/Shortcuts.html)
279
- shortcut) to 4:56pm UTC on February 6, 2016:
71
+ // Location (if available)
72
+ console.log(tags.GPSLatitude, tags.GPSLongitude);
280
73
 
281
- ```js
282
- exiftool.write("path/to/file.jpg", { AllDates: "2016-02-06T16:56:00" });
74
+ // Always check for parsing errors
75
+ if (tags.errors?.length > 0) {
76
+ console.warn("Metadata warnings:", tags.errors);
77
+ }
283
78
  ```
284
79
 
285
- To write to a specific metadata group's tag, just prefix the tag name with the group.
286
- (TypeScript users: you'll need to cast to make this compile).
80
+ ### Writing Metadata
287
81
 
288
- ```js
289
- exiftool.write("path/to/file.jpg", {
290
- "IPTC:CopyrightNotice": "© 2021 PhotoStructure, Inc.",
82
+ ```javascript
83
+ // Add keywords and copyright
84
+ await exiftool.write("photo.jpg", {
85
+ Keywords: ["sunset", "landscape"],
86
+ Copyright: "© 2024 Photographer Name",
87
+ "IPTC:CopyrightNotice": "© 2024 Photographer Name",
291
88
  });
292
- ```
293
89
 
294
- To delete a tag, use `null` as the value.
295
-
296
- ```js
297
- exiftool.write("path/to/file.jpg", { UserComment: null });
298
- ```
299
-
300
- The above example removes any value associated with the `UserComment` tag.
301
-
302
- ### Partial Date Support
303
-
304
- **NEW in v30.2.0:** XMP date tags now support partial dates (year-only or year-month):
305
-
306
- ```js
307
- // Year-only dates
308
- exiftool.write("path/to/file.jpg", { "XMP:CreateDate": 1980 });
309
- // or
310
- exiftool.write("path/to/file.jpg", {
311
- "XMP:CreateDate": ExifDate.fromYear(1980),
90
+ // Update all date fields at once
91
+ await exiftool.write("photo.jpg", {
92
+ AllDates: "2024:03:15 14:30:00",
312
93
  });
313
94
 
314
- // Year-month dates (EXIF format)
315
- exiftool.write("path/to/file.jpg", { "XMP:CreateDate": "1980:08" });
316
- // Year-month dates (ISO format)
317
- exiftool.write("path/to/file.jpg", { "XMP:CreateDate": "1980-08" });
318
- // or
319
- exiftool.write("path/to/file.jpg", {
320
- "XMP:CreateDate": ExifDate.fromYearMonth("1980-08"),
95
+ // Delete tags
96
+ await exiftool.write("photo.jpg", {
97
+ UserComment: null,
321
98
  });
322
99
  ```
323
100
 
324
- **⚠️ IMPORTANT:** Partial dates are only supported for [XMP
325
- tags](https://exiftool.org/TagNames/XMP.html) (like `XMP:CreateDate`,
326
- `XMP:MetadataDate`). [EXIF tags](https://exiftool.org/TagNames/EXIF.html), like
327
- `EXIF:CreateDate`, require **complete** (year, month, and day) dates. Always use
328
- the group-prefixed tag names (e.g., `"XMP:CreateDate"`) when working with
329
- partial dates.
330
-
331
- ### Always Beware: Timezones
101
+ ### Extracting Images
332
102
 
333
- If you edit a timestamp tag, realize that the difference between the
334
- changed timestamp tag and the GPS value is used by `exiftool-vendored` to
335
- infer the timezone.
103
+ ```javascript
104
+ // Extract thumbnail
105
+ await exiftool.extractThumbnail("photo.jpg", "thumbnail.jpg");
336
106
 
337
- In other words, if you only edit the `CreateDate` and don't edit the `GPS`
338
- timestamps, your timezone will either be incorrect or missing. See the
339
- section about [Dates](#dates) below for more information.
107
+ // Extract preview (larger than thumbnail)
108
+ await exiftool.extractPreview("photo.jpg", "preview.jpg");
340
109
 
341
- ### Rewriting tags
342
-
343
- You may find that some of your images have corrupt metadata and that writing
344
- new dates, or editing the rotation information, for example, fails. ExifTool can
345
- try to repair these images by rewriting all the metadata into a new file, along
346
- with the original image content. See the
347
- [documentation](https://exiftool.org/faq.html#Q20) for more
348
- details about this functionality.
349
-
350
- `rewriteAllTags` returns a void Promise that will be rejected if there are any
351
- errors.
352
-
353
- ```js
354
- exiftool.rewriteAllTags("problematic.jpg", "rewritten.jpg");
110
+ // Extract JPEG from RAW files
111
+ await exiftool.extractJpgFromRaw("photo.cr2", "processed.jpg");
355
112
  ```
356
113
 
357
- ### ExifTool configuration support (`.ExifTool_config`)
114
+ ## Understanding Tags
358
115
 
359
- ExifTool has an [extensive user configuration system](http://owl.phy.queensu.ca/~phil/exiftool/config.html). There are several ways to use one:
116
+ The `Tags` interface contains **thousands of metadata fields** from an auto-generated TypeScript file. Each tag uses a special notation:
360
117
 
361
- 1. Place your [user configuration
362
- file](https://exiftool.org/config.html) in your `HOME`
363
- directory
364
- 1. Set the `EXIFTOOL_HOME` environment variable to the fully-qualified path that
365
- contains your user configuration.
366
- 1. Specify the in the ExifTool constructor options:
118
+ ```typescript
119
+ /** ★★★★ Example: 1920 */
120
+ ImageWidth?: number; // Very common, all cameras
367
121
 
368
- ```js
369
- new ExifTool({ exiftoolEnv: { EXIFTOOL_HOME: resolve("path", "to", "config", "dir") }
122
+ /** ★☆☆☆ Example: "Custom" */
123
+ RareTag?: string; // Rare, <1% of files
370
124
  ```
371
125
 
372
- ## Resource hygiene
126
+ - **★★★★** = Found in >50% of files (very common)
127
+ - **★☆☆☆** = Very rare, <1% of files
128
+ - **✔** = Found in popular cameras (Canon, Nikon, Sony, Apple)
373
129
 
374
- **Call `ExifTool.end()` when you're done**
130
+ **Important**: The interface isn't comprehensive - unknown fields may still exist in returned objects.
375
131
 
376
- You must explicitly call
377
- [`.end()`](https://photostructure.github.io/exiftool-vendored.js/classes/ExifTool.html#end)
378
- on any used instance of `ExifTool` to allow `node` to exit gracefully.
132
+ 📖 **[Complete Tags Documentation →](docs/TAGS.md)**
379
133
 
380
- ExifTool child processes consume system resources, and [prevents `node` from
381
- exiting due to the way Node.js streams
382
- work](https://github.com/photostructure/exiftool-vendored.js/issues/106).
134
+ ## Important Notes
383
135
 
384
- Note that you can't call cannot be in a `process.on("exit")` hook, as the `stdio` streams
385
- attached to the child process cannot be `unref`'ed. (If there's a solution to
386
- this, please post to the above issue!)
136
+ ### Dates & Timezones
387
137
 
388
- ### Mocha v4.0.0
138
+ Images rarely specify timezones. This library uses sophisticated heuristics:
389
139
 
390
- If you use [mocha](https://mochajs.org/) v4 or later, and you don't call
391
- `exiftool.end()`, you will find that your test suite hangs. [The relevant
392
- change is described here](https://github.com/mochajs/mocha/issues/3044),
393
- and can be solved by adding an `after` block that shuts down the instance
394
- of ExifTool that your tests are using:
140
+ 1. **Explicit metadata** (TimeZoneOffset, OffsetTime)
141
+ 2. **GPS location** timezone lookup
142
+ 3. **UTC timestamps** → calculate offset
395
143
 
396
- ```js
397
- after(() => exiftool.end()); // assuming your singleton is called `exiftool`
144
+ ```javascript
145
+ const dt = tags.DateTimeOriginal;
146
+ if (dt instanceof ExifDateTime) {
147
+ console.log("Timezone offset:", dt.tzoffset, "minutes");
148
+ console.log("Timezone:", dt.zone);
149
+ }
398
150
  ```
399
151
 
400
- ## Dates
401
-
402
- **The date metadata in all your images and videos are, most likely,
403
- underspecified.**
404
-
405
- Images and videos rarely specify a time zone in their dates. If all your files
406
- were captured in your current time zone, defaulting to the local time zone is a
407
- safe assumption, but if you have files that were captured in different parts of
408
- the world, **this assumption will not be correct**. Parsing the same file in
409
- different parts of the world result in different times for the same file.
410
-
411
- Prior to version 7, heuristic 1 and 3 were applied.
152
+ 📖 **[Date & Timezone Guide →](docs/DATES.md)**
412
153
 
413
- As of version 7.0.0, `exiftool-vendored` uses the following heuristics. The
414
- highest-priority heuristic to return a value will be used as the timezone offset
415
- for all datetime tags that don't already have a specified timezone.
154
+ ### 🧹 Resource Cleanup
416
155
 
417
- ### Heuristic 1: explicit metadata
156
+ **Always call `.end()` on ExifTool instances** to prevent Node.js from hanging:
418
157
 
419
- If the [EXIF](https://exiftool.org/TagNames/EXIF.html)
420
- `TimeZoneOffset` tag is present it will be applied as per the spec to
421
- `DateTimeOriginal`, and if there are two values, the `ModifyDate` tag as well.
422
- `OffsetTime`, `OffsetTimeOriginal`, and `OffsetTimeDigitized` are also
423
- respected, if present (but are very rarely set).
158
+ ```javascript
159
+ import { exiftool } from "exiftool-vendored";
424
160
 
425
- ### Heuristic 2: GPS location
161
+ // Use the singleton
162
+ const tags = await exiftool.read("photo.jpg");
426
163
 
427
- If GPS latitude and longitude is present and valid (the value of `0, 0` is
428
- considered invalid), the `tz-lookup` library will be used to determine the time
429
- zone name for that location.
430
-
431
- ### Heuristic 3: UTC timestamps
432
-
433
- If `GPSDateTime` or `DateTimeUTC` is present, the delta with the dates found
434
- within the file, as long as the delta is valid, is used as the timezone offset.
435
- Deltas of > 14 hours are considered invalid.
436
-
437
- ### ExifDate and ExifDateTime
438
-
439
- Because date-times have this optionally-set timezone, and some tags only specify
440
- the date, this library returns classes that encode the date, the time of day, or
441
- both, **with an optional timezone and an optional tzoffset**: `ExifDateTime` and
442
- `ExifTime`. It's up to you, then, to determine what's correct for your
443
- situation.
444
-
445
- Note also that some smartphones record timestamps with microsecond precision
446
- (not just milliseconds!), and both `ExifDateTime` and `ExifTime` have floating point
447
- milliseconds.
448
-
449
- ## Tags
450
-
451
- Official [EXIF](http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf) tag names
452
- are [PascalCased](https://en.wikipedia.org/wiki/PascalCase), like
453
- `AFPointSelected` and `ISO`. ("Fixing" the field names to be camelCase, would
454
- result in ungainly `aFPointSelected` and `iSO` atrocities).
164
+ // Clean up when done
165
+ process.on("beforeExit", () => exiftool.end());
166
+ ```
455
167
 
456
- The [Tags](https://photostructure.github.io/exiftool-vendored.js/interfaces/Tags.html) interface is
457
- auto-generated by the `mktags` script, which parses through over 6,000 unique
458
- camera make and model images, in large part sourced from the ExifTool site.
459
- `mktags` groups tags, extracts their type, popularity, and example values such
460
- that your IDE can autocomplete.
168
+ #### Automatic Cleanup with Disposable Interfaces
461
169
 
462
- Tags marked with "★★★★", like
463
- [MIMEType](https://photostructure.github.io/exiftool-vendored.js/interfaces/FileTags.html#MIMEType),
464
- should be found in most files. Of the several thousand metadata tags, realize
465
- less than 50 are found generally. You'll need to do your research to
466
- determine which tags are valid for your uses.
170
+ For **TypeScript 5.2+** projects, use automatic resource management:
467
171
 
468
- Note that if parsing fails (for, example, a date-time string), the raw string
469
- will be returned. Consuming code should verify both existence and type as
470
- reasonable for safety.
172
+ ```javascript
173
+ import { ExifTool } from "exiftool-vendored";
471
174
 
472
- ## Serialization
175
+ // Automatic synchronous cleanup
176
+ {
177
+ using et = new ExifTool();
178
+ const tags = await et.read("photo.jpg");
179
+ // ExifTool automatically cleaned up when block exits
180
+ }
473
181
 
474
- The `Tags` object returned by `ExifTool.read()` can be serialized to JSON with `JSON.stringify`.
182
+ // Automatic asynchronous cleanup (recommended)
183
+ {
184
+ await using et = new ExifTool();
185
+ const tags = await et.read("photo.jpg");
186
+ // ExifTool gracefully cleaned up when block exits
187
+ }
188
+ ```
475
189
 
476
- To reconstitute, use the `parseJSON()` method.
190
+ **Benefits:**
477
191
 
478
- ```ts
479
- import { exiftool, parseJSON } from "exiftool-vendored";
192
+ - **Guaranteed cleanup**: No leaked processes, even with exceptions
193
+ - **Timeout protection**: Automatic forceful cleanup if graceful shutdown hangs
194
+ - **Zero boilerplate**: No manual `.end()` calls needed
480
195
 
481
- const tags: Tags = await exiftool.read("/path/to/file.jpg");
482
- const str: string = JSON.stringify(tags);
196
+ ### 🏷️ Tag Completeness
483
197
 
484
- // parseJSON doesn't validate the input, so we don't assert that it's a Tags
485
- // instance, but you can cast it (unsafely...)
198
+ The `Tags` interface shows the most common fields, but ExifTool can extract many more. Cast to access unlisted fields:
486
199
 
487
- const tags2: Tags = parseJSON(str) as Tags;
200
+ ```javascript
201
+ const tags = await exiftool.read("photo.jpg");
202
+ const customField = (tags as any).UncommonTag;
488
203
  ```
489
204
 
490
- ## Performance
205
+ ## Documentation
491
206
 
492
- The default [exiftool]() singleton is intentionally throttled. If full system
493
- utilization is acceptable:
207
+ ### 📚 **Guides**
494
208
 
495
- 1. set
496
- [`maxProcs`](https://photostructure.github.io/batch-cluster.js/classes/BatchClusterOptions.html#maxProcs)
497
- higher
209
+ - **[Installation Guide](docs/INSTALLATION.md)** - Electron, Docker, platform setup
210
+ - **[Usage Examples](docs/USAGE-EXAMPLES.md)** - Comprehensive API examples
211
+ - **[Date Handling](docs/DATES.md)** - Timezone complexities explained
212
+ - **[Tags Reference](docs/TAGS.md)** - Understanding the 2,500+ metadata fields
213
+ - **[Electron Integration](docs/ELECTRON.md)** - Electron-specific setup
498
214
 
499
- 2. consider setting
500
- [`minDelayBetweenSpawnMillis`](https://photostructure.github.io/batch-cluster.js/classes/BatchClusterOptions.html#minDelayBetweenSpawnMillis)
501
- to 0
215
+ ### 🔧 **Troubleshooting**
502
216
 
503
- 3. On a performant linux box, a smaller value of `streamFlushMillis` may work as
504
- well: if you see [`noTaskData`
505
- events](https://photostructure.github.io/batch-cluster.js/interfaces/BatchClusterEvents.html#noTaskData),
506
- you need to bump the value up.
217
+ - **[Debugging Guide](docs/DEBUGGING.md)** - Debug logging and common issues
218
+ - **[Temporal Migration](docs/TEMPORAL-MIGRATION.md)** - Future JavaScript Temporal API
507
219
 
508
- ## Benchmarking
220
+ ### 📖 **API Reference**
509
221
 
510
- The `yarn mktags ../path/to/examples` target reads all tags found in a directory
511
- hierarchy of sample images and videos, and parses the results.
222
+ - **[TypeDoc Documentation](https://photostructure.github.io/exiftool-vendored.js/)** - Complete API reference
512
223
 
513
- `exiftool-vendored` v16.0.0 on a 2019 AMD Ryzen 3900X running Ubuntu 20.04 on an
514
- SSD can process 20+ files per second per thread, or 500+ files per second when
515
- utilizing all CPU threads.
224
+ ## Performance
516
225
 
517
- ### Batch mode
226
+ The default singleton is throttled for stability. For high-throughput processing:
518
227
 
519
- Using ExifTool's `-stay_open` batch mode means we can reuse a single
520
- instance of ExifTool across many requests, dropping response latency
521
- dramatically as well as reducing system load.
228
+ ```javascript
229
+ import { ExifTool } from "exiftool-vendored";
522
230
 
523
- ### Parallelism
231
+ const exiftool = new ExifTool({
232
+ maxProcs: 8, // More concurrent processes
233
+ minDelayBetweenSpawnMillis: 0, // Faster spawning
234
+ streamFlushMillis: 10, // Faster streaming
235
+ });
524
236
 
525
- To avoid overwhelming your system, the `exiftool` singleton is configured with a
526
- `maxProcs` set to a quarter the number of CPUs on the current system (minimally
527
- 1); no more than `maxProcs` instances of `exiftool` will be spawned. If the
528
- system is CPU constrained, however, you may want a smaller value. If you have
529
- very fast disk IO, you may see a speed increase with larger values of
530
- `maxProcs`, but note that each child process can consume 100 MB of RAM.
237
+ // Process many files efficiently
238
+ const results = await Promise.all(filePaths.map((file) => exiftool.read(file)));
531
239
 
532
- ## Author
240
+ await exiftool.end();
241
+ ```
533
242
 
534
- - [Matthew McEachen](https://github.com/mceachen)
243
+ **Benchmarks**: 20+ files/second/thread, 500+ files/second using all CPU cores.
535
244
 
536
- ## Contributors 🎉
245
+ ## Support & Community
537
246
 
538
- - [Joshua Harris](https://github.com/Circuit8)
539
- - [Anton Mokrushin](https://github.com/amokrushin)
540
- - [Luca Ban](https://github.com/mesqueeb)
541
- - [Demiurga](https://github.com/apolkingg8)
542
- - [David Randler](https://github.com/draity)
247
+ - **📋 Issues**: [GitHub Issues](https://github.com/photostructure/exiftool-vendored.js/issues)
248
+ - **📖 Changelog**: [CHANGELOG.md](CHANGELOG.md)
249
+ - **🔒 Security**: [SECURITY.md](SECURITY.md)
250
+ - **📄 License**: [MIT](LICENSE)
543
251
 
544
- ## CHANGELOG
252
+ ### Contributors 🎉
545
253
 
546
- See the
547
- [CHANGELOG](https://github.com/mceachen/exiftool-vendored.js/blob/main/CHANGELOG.md) on github.
254
+ [Matthew McEachen](https://github.com/mceachen), [Joshua Harris](https://github.com/Circuit8), [Anton Mokrushin](https://github.com/amokrushin), [Luca Ban](https://github.com/mesqueeb), [Demiurga](https://github.com/apolkingg8), [David Randler](https://github.com/draity)