UNPKG

exiftool-vendored

Version:
265 lines (186 loc) โ€ข 8 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"); // The singleton instance automatically cleans up on process exit by default. // If you need immediate cleanup or have disabled registerExitHandlers: // 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?package_id=UGFja2FnZS0xNjYxNjY2MQ%3D%3D). ### ๐Ÿ”ง **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 6,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) - **๐ŸงŠ** = 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 โ†’](docs/TAGS.md)** ## Important Notes ### โฐ 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 **Always call `.end()` on ExifTool instances** to prevent Node.js from hanging: ```javascript 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: ```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 ### ๐Ÿท๏ธ 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)