Fast, cross-platform Node.js access to ExifTool. Built and supported by PhotoStructure.
Installation & Quick StartRequirements: 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.
ExifDateTime
classesconst 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;
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:
const dt = tags.DateTimeOriginal; if (dt instanceof ExifDateTime) { console.log("Timezone offset:", dt.tzoffset, "minutes"); console.log("Timezone:", dt.zone); }
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());Automatic Cleanup with Disposable Interfaces
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:
.end()
calls neededThe 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;
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.
Matthew McEachen, Joshua Harris, Anton Mokrushin, Luca Ban, Demiurga, David Randler
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4