exiftool-vendored
    Preparing search index...

    Usage Examples

    Complete examples for common exiftool-vendored use cases.

    import { exiftool } from "exiftool-vendored";
    // or: const { exiftool } = require("exiftool-vendored");

    // Verify installation
    console.log(`ExifTool v${await exiftool.version()}`);

    // Shut down the `exiftool` child process so node can exit cleanly:
    await exiftool.end();
    const tags = await exiftool.read("photo.jpg");

    console.log("Camera:", tags.Make, tags.Model);
    console.log("Size:", tags.ImageWidth, "x", tags.ImageHeight);
    console.log("Taken:", tags.DateTimeOriginal);
    console.log("Location:", tags.GPSLatitude, tags.GPSLongitude);
    const tags = await exiftool.read("photo.jpg");

    // Handle optional values safely
    const camera = tags.Make ? `${tags.Make} ${tags.Model}` : "Unknown camera";
    const dimensions =
    tags.ImageWidth && tags.ImageHeight
    ? `${tags.ImageWidth}x${tags.ImageHeight}`
    : "Unknown size";

    // Use nullish coalescing for fallbacks
    const timestamp = tags.DateTimeOriginal ?? tags.DateTime ?? tags.FileModifyDate;
    const title = tags.Title ?? tags.DocumentName ?? tags.FileName;
    try {
    const tags = await exiftool.read("photo.jpg");

    // Check for parsing warnings
    if (tags.errors && tags.errors.length > 0) {
    console.warn("Metadata warnings:", tags.errors);
    }

    console.log("Successfully read metadata");
    } catch (error) {
    console.error("Failed to read file:", error.message);
    }
    // Add comment and copyright
    await exiftool.write("photo.jpg", {
    XPComment: "Beautiful sunset",
    Copyright: "© 2024 Your Name",
    });

    // Update capture date
    await exiftool.write("photo.jpg", {
    DateTimeOriginal: "2024:03:15 14:30:00",
    });
    // Write to specific metadata groups
    await exiftool.write("photo.jpg", {
    "IPTC:Keywords": "sunset, landscape, nature",
    "IPTC:CopyrightNotice": "© 2024 Photographer Name",
    "XMP:Title": "Sunset Over Mountains",
    "XMP:Description": "A stunning sunset captured in the mountains",
    });
    // Delete specific tags by setting to null
    await exiftool.write("photo.jpg", {
    UserComment: null,
    ImageDescription: null,
    "IPTC:Keywords": null,
    });
    // Update all date fields at once
    await exiftool.write("photo.jpg", {
    AllDates: "2024:03:15 14:30:00",
    });

    // This is equivalent to setting:
    // - DateTimeOriginal
    // - CreateDate
    // - ModifyDate
    // Set GPS location (decimal degrees)
    await exiftool.write("photo.jpg", {
    GPSLatitude: 40.7128,
    GPSLongitude: -74.006,
    GPSAltitude: 10, // meters above sea level
    });
    // Extract EXIF thumbnail
    try {
    await exiftool.extractThumbnail("photo.jpg", "thumbnail.jpg");
    console.log("Thumbnail extracted successfully");
    } catch (error) {
    console.log("No thumbnail found or extraction failed");
    }
    // Extract preview image (larger than thumbnail)
    try {
    await exiftool.extractPreview("photo.jpg", "preview.jpg");
    console.log("Preview extracted successfully");
    } catch (error) {
    console.log("No preview found or extraction failed");
    }
    import { parseJSON, ExifDateTime } from "exiftool-vendored";
    import { readFile, writeFile } from "node:fs/promises";

    // Read and serialize
    const tags = await exiftool.read("photo.jpg");
    const jsonString = JSON.stringify(tags);

    // Save to file or send over network
    await writeFile("metadata.json", jsonString);

    // Later, deserialize
    const savedJson = await readFile("metadata.json", "utf8");
    const restoredTags = parseJSON(savedJson);

    // restoredTags has proper ExifDateTime objects restored
    console.log(restoredTags.DateTimeOriginal instanceof ExifDateTime); // true

    Node.js will not exit cleanly while any ExifTool instance has any running exiftool child processes due to the way that Node.js handles stdin/stdout/stderr streams.

    At least with this library, it's up to you end what you start.

    Note: depending on the platform, starting and ending an exiftool instance may be (very!) time consuming -- like, 5-30 seconds on Windows -- so ultrathink your code a bit to ensure you aren't spawning and killing exiftool instances needlessly.

    yes I said ultrathink as if it was Proper English but you know you thought it was funny

    import { ExifTool } from "exiftool-vendored";

    const exiftool = new ExifTool();

    try {
    const tags = await exiftool.read("photo.jpg");
    console.log(tags.Make, tags.Model);
    } finally {
    // Always clean up to prevent hanging processes
    await exiftool.end();
    }

    For TypeScript 5.2+ projects with proper tsconfig.json configuration:

    {
    "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM"]
    }
    }
    import { ExifTool } from "exiftool-vendored";

    // Block scope with automatic cleanup
    {
    using et = new ExifTool();
    const tags = await et.read("photo.jpg");
    console.log(`Camera: ${tags.Make} ${tags.Model}`);
    // ExifTool.end(false) called automatically when block exits
    }

    // Multiple files with automatic cleanup
    function processPhotos(filePaths) {
    using et = new ExifTool({ maxProcs: 4 });

    return Promise.all(
    filePaths.map(async (file) => {
    const tags = await et.read(file);
    return { file, camera: `${tags.Make} ${tags.Model}` };
    }),
    );
    // Cleanup happens even if Promise.all() throws
    }
    import { ExifTool } from "exiftool-vendored";

    // Graceful cleanup with timeout protection
    {
    await using et = new ExifTool();

    const tags = await et.read("photo.jpg");

    await et.write("photo.jpg", {
    XPComment: "Processed with exiftool-vendored, golly gee whiz it's neato",
    Copyright: "© 2024",
    });

    // ExifTool.end(true) called automatically with timeout protection
    // If graceful cleanup times out, forceful cleanup is attempted
    }

    // Function with automatic cleanup
    async function batchProcessPhotos(filePaths) {
    await using et = new ExifTool({
    maxProcs: 8,
    taskTimeoutMillis: 30000,
    });

    const results = [];

    for (const file of filePaths) {
    try {
    const tags = await et.read(file);

    // Add copyright
    await et.write(file, {
    Copyright: "© 2025 Your Company",
    });

    results.push({ file, success: true, camera: tags.Make });
    } catch (error) {
    results.push({ file, success: false, error: error.message });
    }
    }

    return results;
    // Automatic cleanup happens here, even with exceptions
    }
    import { ExifTool } from "exiftool-vendored";

    async function robustProcessing(file) {
    try {
    await using et = new ExifTool();

    const tags = await et.read(file);

    if (tags.errors?.length > 0) {
    console.warn(`Metadata warnings for ${file}:`, tags.errors);
    }

    return tags;
    } catch (error) {
    if (error.message.includes("ENOENT")) {
    throw new Error(`File not found: ${file}`);
    }
    throw error;
    }
    // ExifTool cleanup guaranteed, even with exceptions
    }
    import { ExifTool } from "exiftool-vendored";

    // Custom timeout configuration
    {
    await using et = new ExifTool({
    disposalTimeoutMs: 2000, // 2 seconds for sync disposal
    asyncDisposalTimeoutMs: 30_000, // 30 seconds for async disposal
    });

    // Your processing here
    const tags = await et.read("large-file.tiff");
    }
    1. Guaranteed Cleanup: Resources are freed even if exceptions occur
    2. Timeout Protection: Automatic forceful cleanup if graceful shutdown hangs
    3. Zero Boilerplate: No manual .end() calls or complex try/finally blocks
    4. Scope-Based: Clear resource lifetime tied to lexical scope
    5. Exception Safe: Works correctly with async/await exception handling
    • using: Simple synchronous cleanup, fire-and-forget scenarios
    • await using: Production code requiring graceful cleanup (recommended)
    • Manual .end(): Pre-TypeScript 5.2 environments or fine-grained control