exiftool-vendored
    Preparing search index...

    exiftool-vendored

    exiftool-vendored

    Fast, cross-platform Node.js access to ExifTool. Built and supported by PhotoStructure.

    npm version Node.js CI GitHub issues

    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:

    1. Explicit metadata (TimeZoneOffset, OffsetTime)
    2. GPS location โ†’ timezone lookup
    3. 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;

    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