Fast, cross-platform Node.js access to ExifTool. Built and supported by PhotoStructure.
Requirements: Node.js Active LTS or Maintenance LTS versions only
npm install exiftool-vendored
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");
// The singleton instance automatically cleans up on process exit by default.
// If you need immediate cleanup or have disabled registerExitHandlers:
// await exiftool.end();
Order of magnitude faster than other Node.js ExifTool modules. Powers PhotoStructure and 1,000+ other projects.
- Cross-platform: macOS, Linux, Windows
- Comprehensive: Read, write, extract embedded images
- Reliable: Battle-tested with extensive test coverage
- TypeScript: Full type definitions for thousands of metadata fields
- Smart dates: Timezone-aware
ExifDateTime
classes - Auto-generated tags: Based on 6,000+ real camera samples
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);
}
// 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,
});
// 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");
The Tags
interface contains thousands of metadata fields from an auto-generated TypeScript file. Each tag includes semantic JSDoc annotations:
/**
* @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)
- π§ = 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
Important: The interface isn't comprehensive - unknown fields may still exist in returned objects.
π Complete Tags Documentation β
Images rarely specify timezones. This library uses sophisticated heuristics:
- Explicit metadata (TimeZoneOffset, OffsetTime)
- GPS location β timezone lookup
- UTC timestamps β calculate offset
const dt = tags.DateTimeOriginal;
if (dt instanceof ExifDateTime) {
console.log("Timezone offset:", dt.tzoffset, "minutes");
console.log("Timezone:", dt.zone);
}
π Date & Timezone Guide β
Always call .end()
on ExifTool instances to prevent Node.js from hanging:
import { exiftool } from "exiftool-vendored";
// Use the singleton
const tags = await exiftool.read("photo.jpg");
// Clean up when done
process.on("beforeExit", () => exiftool.end());
For TypeScript 5.2+ projects, use automatic resource management:
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
The Tags
interface shows the most common fields, but ExifTool can extract many more. Cast to access unlisted fields:
const tags = await exiftool.read("photo.jpg");
const customField = (tags as any).UncommonTag;
- Installation Guide - Electron, Docker, platform setup
- Usage Examples - Comprehensive API examples
- Date Handling - Timezone complexities explained
- Tags Reference - Understanding the 2,500+ metadata fields
- Electron Integration - Electron-specific setup
- Debugging Guide - Debug logging and common issues
- Temporal Migration - Future JavaScript Temporal API
- TypeDoc Documentation - Complete API reference
The default singleton is throttled for stability. For high-throughput processing:
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.
- π Issues: GitHub Issues
- π Changelog: CHANGELOG.md
- π Security: SECURITY.md
- π License: MIT
Matthew McEachen, Joshua Harris, Anton Mokrushin, Luca Ban, Demiurga, David Randler