exiftool-vendored
Blazing-fast, cross-platform Node access to ExifTool.

Features
-
High performance via -stay_open
and multithreading.
7-300x faster performance than competing packages (depending on platform)!
-
Support for Mac, Linux, and Windows.
-
Proper extraction of
-
Robust type definitions of the top 99.5% tags used by over 6,000
different camera makes and models
-
Auditable ExifTool source code (the "vendored" code is
verifiable)
-
Automated updates to ExifTool (as new versions come out
monthly)
-
Robust test suite, performed with Node v4, v6 and v7 on Linux,
Mac, &
Windows.
Installation
npm install --save exiftool-vendored
The vendored version of ExifTool relevant for your platform will be installed
via
platform-dependent-modules.
You shouldn't include either the exiftool-vendored.exe
or
exiftool-vendored.pl
as direct dependencies to your project.
Usage
import { exiftool } from "exiftool-vendored";
exiftool
.read("path/to/image.jpg")
.then((tags ) => console.log(`Make: ${tags.Make}, Model: ${tags.Model}`))
.catch(err => console.error("Something terrible happened: ", err))
exiftool.extractThumbnail("path/to/image.jpg", "path/to/thumbnail.jpg");
exiftool.extractPreview("path/to/image.jpg", "path/to/preview.jpg");
exiftool.extractJpgFromRaw("path/to/image.cr2", "path/to/fromRaw.jpg");
exiftool.extractBinaryTag("tagname", "path/to/file.exf", "path/to/dest.bin");
Performance
With the npm run mktags
target, > 3000 sample images, and maxProcs
set to 4,
reading tags on my laptop takes ~6 ms per image:
Read 2236 unique tags from 3011 files.
Parsing took 16191ms (5.4ms / file)
Parsing took 27141ms (9.0ms / file)
Parsing took 12545ms (4.2ms / file)
For reference, using the exiftool
npm package (which doesn't work on Windows)
took 85 seconds (almost 7x the time):
Reading 3011 files...
Parsing took 85654ms (28.4ms / file)
This package is so much faster due to ExifTool
stay_open and support for > 1
child processes.
stay_open
Starting the perl version of ExifTool is expensive, and is especially
expensive on the Windows version of ExifTool.
On Windows, a distribution of Perl and the ~1000 files that make up ExifTool are
extracted into a temporary directory for every invocation. Windows virus
scanners that wedge reads on these files until they've been determined to be
safe make this approach even more costly.
Using -stay_open
we can reuse a single instance of ExifTool across all
requests, which drops response latency dramatically.
Parallelism
The exiftool
singleton is configured with a maxProcs
of 1; no more than 1
child process of ExifTool will be spawned, even if there are many read requests
outstanding.
If you want higher throughput, instantiate your own singleton reference of
ExifTool
with a higher maxProcs. Note that exceeding your cpu count won't
increase throughput, and that each child process consumes between 10 and 50 MB
of RAM.
You may want to call .end()
on your singleton reference when your script
terminates. This gracefully shuts down all child processes.
Dates
Generally, EXIF tags encode dates and times with no timezone offset.
Presumably the time is captured in local time, but this means parsing the same
file in different parts of the world results in a different absolute timestamp
for the same file.
Rather than returning a
Date
which always includes a timezone, this library returns classes that encode the
date, the time of day, or both, with an optional tzoffset. It's up to you, then,
to do what's right.
In many cases, though, a tzoffset can be determined, either by the composite
TimeZone
tag, or by looking at the difference between the local
DateTimeOriginal
and GPSDateTime
tags. GPSDateTime
is present in most
smartphone images.
If a tzoffset can be determined, it is encoded in all related ExifDateTime
tags for those files.
Tags
Official EXIF tag names
are PascalCased, like
AFPointSelected
and ISO
. ("Fixing" the field names to be camelCase, would
result in ungainly aFPointSelected
and iSO
atrocities).
The tags.ts
file is autogenerated by parsing through images of more than 3,000
different camera makes and models taken from the ExifTool site. It groups tags,
their type, frequency, and example values such that your IDE can autocomplete.
Note that tag existence and types is not guaranteed. If parsing fails (for,
example, and datetime string), the raw string will be returned. Consuming code
should verify both existence and type as reasonable for safety.
Logging
debug is used with the
exiftool-vendored
prefix.
Versioning
I wanted to include the ExifTool's version number explicitly in the version
number, but npm requires strict compliance with SemVer. Given that ExifTool
sometimes includes patch releases, there aren't always enough spots to encode an
API version and the ExifTool version.
Given those constraints, version numbers follow standard SemVer, with the
following scheme:
API.UPDATE.PATCH
API
is incremented for
- š Non-backward-compatible API changes
- š² New releases of ExifTool that have externally visible changes
UPDATE
is incremented for
- š± New releases of ExifTool with no externally visible changes
- š Bugfixes
- āØ Backward-compatible features
PATCH
is incremented for
- š¦ Minor packaging changes
- š Minor bugfixes
Changelog
v2.15.0
- āØ Added example movies to the sample image corpus used to build the tag
definitions.
v2.14.0
- āØ Added
taskTimeoutMillis
, which will cause the promise to be rejected if
exiftool takes longer than this value to parse the file. Note that this
timeout only starts "ticking" when the task is enqueued to an idle ExifTool
process. - āØ Pump the
onIdle
method every onIdleIntervalMillis
(defaults to every 10
seconds) to ensure all requested tasks are serviced. - āØ If
ECONN
or ECONNRESET
is raised from the child process (which seems to
happen for roughly 1% of requests), the current task is re-enqueued and the
current exiftool process is recycled. - āØ Rebuilt
Tags
definitions using more (6,412!) sample image files
(via npm run mktags ~/sample-images
), including many RAW image types
(like .ORF
, .CR2
, and .NEF
).
v2.13.0
- āØ Added
maxReuses
before exiftool processes are recycled - š± ExifTool upgraded to v10.50
v2.12.0
v2.11.0
- š± ExifTool upgraded to v10.47
- āØ Added call to
.kill()
on .end()
in case the stdin command was missed by ExifTool
v2.10.0
- āØ Added support for Node 4. TypeScript builds under es5 mode.
v2.9.0
- š± ExifTool upgraded to v10.46
v2.8.0
- š± ExifTool upgraded to v10.44
- š¦ Upgraded to TypeScript 2.2
- š
update/io.ts
error message didn't handle null statuscodes properly - š
update/mktags.ts
had a counting bug exposed by TS 2.2
v2.7.0
- āØ More robust error handling for child processes (previously there was no
.on("error")
added to the process itself, only on stderr
of the child
process).
v2.6.0
- š± ExifTool upgraded to v10.41
- āØ
Orientation
is rendered as a string by
ExifTool,
which was surprising (to me, at least). By exposing optional args in
ExifTool.read
, the caller can choose how ExifTool renders tag values.
v2.5.0
- š
LANG
and LC_
environment variables were passed through to exiftool (and
subsequently, perl). These are now explicitly unset when exec'ing ExifTool,
both to ensure tag names aren't internationalized, and to prevent perl errors
from bubbling up to the caller due to missing locales.
v2.4.0
- āØ
extractBinaryTag
exposed because there are a lot of binary tags (and
they aren't all embedded images) - š
JpgFromRaw
was missing in Tag
(no raw images were in the example corpus!)
v2.3.0
- āØ
extractJpgFromRaw
implemented to pull out EXIF-embedded images from RAW formats
v2.2.0
- š± ExifTool upgraded to v10.40
v2.1.1
- āØ
extractThumbnail
and extractPreview
implemented to pull out EXIF-embedded images - š¦ Rebuilt package.json.files section
v2.0.1
- š Switched from home-grown
logger
to debug
v1.5.3
- š¦ Switch back to
platform-dependent-modules
.
npm warnings
aren't awesome. - š¦ Don't include tests or updater in the published package
v1.5.0
- š± ExifTool upgraded to v10.38
- š¦ Use
npm
's os-specific optionalDependencies rather than platform-dependent-modules
.
v1.4.1
- š Several imports (like
process
) name-collided on the globals imported by Electron
v1.4.0
- š± ExifTool upgraded to v10.37
v1.3.0
- š± ExifTool upgraded to v10.36
- āØ
Tag.Error
exposed for unsupported file types.
v1.2.0
- š It was too easy to miss calling
ExifTool.end()
, which left child ExifTool
processes running. The constructor to ExifTool now adds a shutdown hook to
send all child processes a shutdown signal.
v1.1.0
- āØ Added
toString()
for all date/time types
v1.0.0
- āØ Added typings reference in the package.json
- š± Upgraded vendored exiftool to 10.33
v0.4.0
- š¦ Fixed packaging (maintain jsdocs in .d.ts and added typings reference)
- š¦ Using np for packaging
v0.3.0
- āØ Multithreading support with the
maxProcs
ctor param - āØ Added tests for reading images with truncated or missing EXIF headers
- āØ Added tests for timezone offset extraction and rendering
- āØ Subsecond resolution from the Google Pixel has 8 significant digits(!!),
added support for that.
v0.2.0
- āØ More rigorous TimeZone extraction from assets, and added the
ExifTimeZoneOffset
to handle the TimeZone
composite tag - āØ Added support for millisecond timestamps
v0.1.1
š±āØ Initial Release. Packages ExifTool v10.31.