exiftool-vendored
Version:
Efficient, cross-platform access to ExifTool
298 lines (207 loc) โข 9.22 kB
Markdown
# exiftool-vendored
**Fast, cross-platform [Node.js](https://nodejs.org/) access to [ExifTool](https://exiftool.org/). Built and supported by [PhotoStructure](https://photostructure.com).**
[](https://www.npmjs.com/package/exiftool-vendored)
[](https://github.com/photostructure/exiftool-vendored.js/actions/workflows/build.yml)
[](https://github.com/photostructure/exiftool-vendored.js/issues)
## Installation & Quick Start
**Requirements**: Node.js Active LTS or Maintenance LTS versions only
```bash
npm install exiftool-vendored
```
```javascript
import { exiftool } from "exiftool-vendored";
// Read metadata
const tags = await exiftool.read("photo.jpg");
console.log(`Camera: ${tags.Make} ${tags.Model}`);
console.log(`Taken: ${tags.DateTimeOriginal}`);
console.log(`Size: ${tags.ImageWidth}x${tags.ImageHeight}`);
// Write metadata
await exiftool.write("photo.jpg", {
XPComment: "Amazing sunset!",
Copyright: "ยฉ 2024 Your Name",
});
// Extract thumbnail
await exiftool.extractThumbnail("photo.jpg", "thumb.jpg");
await exiftool.end();
```
## Why exiftool-vendored?
### โก **Performance**
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).
### ๐ง **Robust**
- **Cross-platform**: macOS, Linux, Windows
- **Comprehensive**: Read, write, extract embedded images
- **Reliable**: Battle-tested with extensive test coverage
### ๐ **Developer-Friendly**
- **TypeScript**: Full type definitions for thousands of metadata fields
- **Smart dates**: Timezone-aware `ExifDateTime` classes
- **Auto-generated tags**: Based on 10,000+ real camera samples
## Core Features
### Reading Metadata
```javascript
const tags = await exiftool.read("photo.jpg");
// Camera info
console.log(tags.Make, tags.Model, tags.LensModel);
// Capture settings
console.log(tags.ISO, tags.FNumber, tags.ExposureTime);
// Location (if available)
console.log(tags.GPSLatitude, tags.GPSLongitude);
// Always check for parsing errors
if (tags.errors?.length > 0) {
console.warn("Metadata warnings:", tags.errors);
}
```
### Writing Metadata
```javascript
// Add keywords and copyright
await exiftool.write("photo.jpg", {
Keywords: ["sunset", "landscape"],
Copyright: "ยฉ 2024 Photographer Name",
"IPTC:CopyrightNotice": "ยฉ 2024 Photographer Name",
});
// Update all date fields at once
await exiftool.write("photo.jpg", {
AllDates: "2024:03:15 14:30:00",
});
// Delete tags
await exiftool.write("photo.jpg", {
UserComment: null,
});
```
### Extracting Images
```javascript
// Extract thumbnail
await exiftool.extractThumbnail("photo.jpg", "thumbnail.jpg");
// Extract preview (larger than thumbnail)
await exiftool.extractPreview("photo.jpg", "preview.jpg");
// Extract JPEG from RAW files
await exiftool.extractJpgFromRaw("photo.cr2", "processed.jpg");
```
## Understanding Tags
The `Tags` interface contains **thousands of metadata fields** from an auto-generated TypeScript file. Each tag includes semantic JSDoc annotations:
```typescript
/**
* @frequency ๐ฅ โ
โ
โ
โ
(85%)
* @groups EXIF, MakerNotes
* @example 100
*/
ISO?: number;
/**
* @frequency ๐ง โ
โ
โ
โ (23%)
* @groups MakerNotes
* @example "Custom lens data"
*/
LensSpec?: string;
```
- **๐ฅ** = Found on mainstream devices (iPhone, Canon, Nikon, Sony)
- **๐ง** = Only found on more obscure camera makes and models
- **โ
โ
โ
โ
** = Found in >50% of files, **โโโโ** = rare (<1%)
- **@groups** = Metadata categories (EXIF, GPS, IPTC, XMP, etc.)
- **@example** = Representative values
### Code defensively!
- No fields are guaranteed to be present.
- Value types are not guaranteed -- assume strings may get in your numeric fields, and handle it gracefully.
- There may very well be keys returned that are **not** in the `Tags` interface.
๐ **[Complete Tags Documentation โ](docs/TAGS.md)**
## Important Notes
### โ๏ธ Configuration
exiftool-vendored provides two levels of configuration:
**Library-wide Settings** - Global configuration affecting all instances:
```javascript
import { Settings } from "exiftool-vendored";
// Enable parsing of archaic timezone offsets for historical photos
Settings.allowArchaicTimezoneOffsets.value = true;
```
**Per-instance Options** - Configuration for individual ExifTool instances:
```javascript
import { ExifTool } from "exiftool-vendored";
const exiftool = new ExifTool({
maxProcs: 8, // More concurrent processes
useMWG: true, // Use Metadata Working Group tags
backfillTimezones: true, // Infer missing timezones
});
```
๐ **[Complete Configuration Guide โ](docs/CONFIGURATION.md)**
### โฐ Dates & Timezones
Images rarely specify timezones. This library uses sophisticated heuristics:
1. **Explicit metadata** (TimeZoneOffset, OffsetTime)
2. **GPS location** โ timezone lookup
3. **UTC timestamps** โ calculate offset
```javascript
const dt = tags.DateTimeOriginal;
if (dt instanceof ExifDateTime) {
console.log("Timezone offset:", dt.tzoffset, "minutes");
console.log("Timezone:", dt.zone);
}
```
๐ **[Date & Timezone Guide โ](docs/DATES.md)**
### ๐งน Resource Cleanup
As of v35, **Node.js will exit naturally** without calling `.end()` โ child processes are cleaned up automatically when the parent exits.
For **long-running applications** (servers, daemons), calling `.end()` is still recommended for graceful shutdown:
```javascript
import { exiftool } from "exiftool-vendored";
// For servers/daemons: graceful shutdown on termination signals
process.on("SIGINT", () => exiftool.end());
process.on("SIGTERM", () => exiftool.end());
```
#### Automatic Cleanup with Disposable Interfaces
For **TypeScript 5.2+** projects, consider using automatic resource management:
```javascript
import { ExifTool } from "exiftool-vendored";
// Automatic synchronous cleanup
{
using et = new ExifTool();
const tags = await et.read("photo.jpg");
// ExifTool automatically cleaned up when block exits
}
// Automatic asynchronous cleanup (recommended)
{
await using et = new ExifTool();
const tags = await et.read("photo.jpg");
// ExifTool gracefully cleaned up when block exits
}
```
**Benefits:**
- **Guaranteed cleanup**: No leaked processes, even with exceptions
- **Timeout protection**: Automatic forceful cleanup if graceful shutdown hangs
- **Zero boilerplate**: No manual `.end()` calls needed
**Caution:**
- **Operating-system startup lag**: Linux costs ~50-500ms to launch a new ExifTool process, but macOS can take several seconds (presumably due to Gatekeeper), and **Windows can take tens of seconds** due to antivirus shenanigans. Don't dispose your instance unless you're **really** done with it!
### ๐ท๏ธ Tag Completeness
The `Tags` interface shows the most common fields, but ExifTool can extract many more. Cast to access unlisted fields:
```javascript
const tags = await exiftool.read("photo.jpg");
const customField = (tags as any).UncommonTag;
```
## Documentation
### ๐ **Guides**
- **[Installation Guide](docs/INSTALLATION.md)** - Electron, Docker, platform setup
- **[Usage Examples](docs/USAGE-EXAMPLES.md)** - Comprehensive API examples
- **[Date Handling](docs/DATES.md)** - Timezone complexities explained
- **[Tags Reference](docs/TAGS.md)** - Understanding the 2,500+ metadata fields
- **[Electron Integration](docs/ELECTRON.md)** - Electron-specific setup
### ๐ง **Troubleshooting**
- **[Debugging Guide](docs/DEBUGGING.md)** - Debug logging and common issues
- **[Temporal Migration](docs/TEMPORAL-MIGRATION.md)** - Future JavaScript Temporal API
### ๐ **API Reference**
- **[TypeDoc Documentation](https://photostructure.github.io/exiftool-vendored.js/)** - Complete API reference
## Performance
The default singleton is throttled for stability. For high-throughput processing:
```javascript
import { ExifTool } from "exiftool-vendored";
const exiftool = new ExifTool({
maxProcs: 8, // More concurrent processes
minDelayBetweenSpawnMillis: 0, // Faster spawning
streamFlushMillis: 10, // Faster streaming
});
// Process many files efficiently
const results = await Promise.all(filePaths.map((file) => exiftool.read(file)));
await exiftool.end();
```
**Benchmarks**: 20+ files/second/thread, 500+ files/second using all CPU cores.
## Support & Community
- **๐ Issues**: [GitHub Issues](https://github.com/photostructure/exiftool-vendored.js/issues)
- **๐ Changelog**: [CHANGELOG.md](CHANGELOG.md)
- **๐ Security**: [SECURITY.md](SECURITY.md)
- **๐ License**: [MIT](LICENSE)
### Contributors ๐
[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)