UNPKG

exiftool-vendored

Version:
298 lines (207 loc) โ€ข 9.22 kB
# exiftool-vendored **Fast, cross-platform [Node.js](https://nodejs.org/) access to [ExifTool](https://exiftool.org/). Built and supported by [PhotoStructure](https://photostructure.com).** [![npm version](https://img.shields.io/npm/v/exiftool-vendored.svg)](https://www.npmjs.com/package/exiftool-vendored) [![Node.js CI](https://github.com/photostructure/exiftool-vendored.js/actions/workflows/build.yml/badge.svg)](https://github.com/photostructure/exiftool-vendored.js/actions/workflows/build.yml) [![GitHub issues](https://img.shields.io/github/issues/photostructure/exiftool-vendored.js.svg)](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)