exiftool-vendored
Advanced tools
Comparing version 4.7.1 to 4.8.0
@@ -27,2 +27,9 @@ # Changelog | ||
### v4.8.0 | ||
* ✨ Corrected the type interface to `ExifTool.write()` to be only string or | ||
numeric values with keys from `Tags` so intellisense can work it's magicks | ||
* 📦 Updated the README with more examples | ||
* 📦 Added timestamp write tests | ||
### v4.7.1 | ||
@@ -29,0 +36,0 @@ |
@@ -97,2 +97,2 @@ export declare function compact<T>(array: (T | undefined | null)[]): T[]; | ||
} | ||
export declare function parse(tagName: string, rawTagValue: string, globalTzOffset?: number): ExifDate | ExifTime | ExifDateTime | ExifTimeZoneOffset | string; | ||
export declare function parse(tagName: string, rawTagValue: string, globalTzOffsetMinutes?: number): ExifDate | ExifTime | ExifDateTime | ExifTimeZoneOffset | string; |
@@ -217,3 +217,5 @@ "use strict"; | ||
var _a = pad2(this.month, this.day, this.hour, this.minute, this.second), mo = _a[0], da = _a[1], ho = _a[2], mi = _a[3], se = _a[4]; | ||
return this.year + "-" + mo + "-" + da + "T" + ho + ":" + mi + ":" + se + millisToFractionalPart(this.millis, millisPrecision) + this.tz(this.tzoffsetMinutes); | ||
var ms = millisToFractionalPart(this.millis, millisPrecision); | ||
var tz = this.tz(this.tzoffsetMinutes); | ||
return this.year + "-" + mo + "-" + da + "T" + ho + ":" + mi + ":" + se + ms + tz; | ||
}; | ||
@@ -276,3 +278,3 @@ // The timezone offset will be extricated prior to this regex: | ||
var emptyRe = /^[\s:0]*$/; // Empty datetimes come back as " : : " or "00:00:00" | ||
function parse(tagName, rawTagValue, globalTzOffset) { | ||
function parse(tagName, rawTagValue, globalTzOffsetMinutes) { | ||
if (rawTagValue === undefined || emptyRe.exec(rawTagValue)) { | ||
@@ -286,3 +288,3 @@ return rawTagValue; | ||
} | ||
var tzoffset = compact([tz.tzOffsetMinutes, globalTzOffset])[0]; | ||
var tzoffset = compact([tz.tzOffsetMinutes, globalTzOffsetMinutes])[0]; | ||
var tagValue = tz.inputWithoutTimezone; | ||
@@ -289,0 +291,0 @@ return (ExifDateTime.for(tagValue, tzoffset) || |
@@ -6,2 +6,5 @@ import { ExifToolTask } from "./ExifToolTask"; | ||
export { ExifDate, ExifTime, ExifDateTime, ExifTimeZoneOffset } from "./DateTime"; | ||
export declare type WriteTags = { | ||
[K in keyof Tags]: string | number; | ||
}; | ||
/** | ||
@@ -76,3 +79,3 @@ * Manages delegating calls to a vendored running instance of ExifTool. | ||
*/ | ||
write(file: string, tags: Tags, args?: string[]): Promise<void>; | ||
write(file: string, tags: WriteTags, args?: string[]): Promise<void>; | ||
/** | ||
@@ -79,0 +82,0 @@ * Extract the low-resolution thumbnail in `path/to/image.jpg` |
@@ -7,3 +7,3 @@ import { ExifToolTask } from "./ExifToolTask"; | ||
private readonly tags; | ||
private tzoffset; | ||
private tzoffsetMinutes; | ||
private constructor(); | ||
@@ -10,0 +10,0 @@ static for(filename: string, optionalArgs?: string[]): ReadTask; |
@@ -61,3 +61,3 @@ "use strict"; | ||
if (tze.tzOffsetMinutes !== undefined) { | ||
this.tzoffset = tze.tzOffsetMinutes; | ||
this.tzoffsetMinutes = tze.tzOffsetMinutes; | ||
} | ||
@@ -71,3 +71,6 @@ else if (this.rawTags.GPSDateTime != null && this.rawTags.DateTimeOriginal != null) { | ||
var localToHalfHour = local.toDate().getTime() / (30 * 60 * 1000); | ||
this.tzoffset = 30 * Math.round(localToHalfHour - gpsToHalfHour); | ||
var tzoffsetMinutes = 30 * Math.round(localToHalfHour - gpsToHalfHour); | ||
if (reasonableTzOffsetMinutes(tzoffsetMinutes)) { | ||
this.tzoffsetMinutes = tzoffsetMinutes; | ||
} | ||
} | ||
@@ -99,3 +102,3 @@ } | ||
else if (typeof value === "string" && tagName.includes("Date") || tagName.includes("Time")) { | ||
return _dt.parse(tagName, value, this.tzoffset); | ||
return _dt.parse(tagName, value, this.tzoffsetMinutes); | ||
} | ||
@@ -125,2 +128,7 @@ else if (tagName.endsWith("GPSLatitude") || tagName.endsWith("GPSLongitude")) { | ||
exports.ReadTask = ReadTask; | ||
function reasonableTzOffsetMinutes(tzOffsetMinutes) { | ||
// Pacific/Kiritimati is +14:00 TIL | ||
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | ||
return Math.abs(tzOffsetMinutes) <= 14 * 60; | ||
} | ||
//# sourceMappingURL=ReadTask.js.map |
@@ -0,3 +1,3 @@ | ||
import { WriteTags } from "./ExifTool"; | ||
import { ExifToolTask } from "./ExifToolTask"; | ||
import { Tags } from "./Tags"; | ||
export declare class WriteTask extends ExifToolTask<void> { | ||
@@ -7,5 +7,5 @@ readonly sourceFile: string; | ||
private constructor(); | ||
static for(filename: string, tags: Tags, optionalArgs?: string[]): WriteTask; | ||
static for(filename: string, tags: WriteTags, optionalArgs?: string[]): WriteTask; | ||
toString(): string; | ||
protected parse(data: string): void; | ||
} |
@@ -27,6 +27,5 @@ "use strict"; | ||
var sourceFile = _path.resolve(filename); | ||
var t = tags; | ||
var args = Object.keys(t) | ||
var args = Object.keys(tags) | ||
.filter(function (k) { return typeof k === "string" && tags.propertyIsEnumerable(k); }) | ||
.map(function (key) { return "-" + key + "=" + t[key]; }); | ||
.map(function (key) { return "-" + key + "=" + tags[key]; }); | ||
optionalArgs.forEach(function (ea) { return args.push(ea); }); | ||
@@ -33,0 +32,0 @@ args.push(sourceFile); |
{ | ||
"name": "exiftool-vendored", | ||
"version": "4.7.1", | ||
"version": "4.8.0", | ||
"description": "Efficient, cross-platform access to ExifTool", | ||
@@ -5,0 +5,0 @@ "main": "./dist/ExifTool.js", |
153
README.md
@@ -18,5 +18,7 @@ # exiftool-vendored | ||
- **latitudes & longitudes** as floats (where negative values indicate west or south of the meridian) | ||
- **embedded images**, both `Thumbnail` and `Preview` (if they exist) | ||
- **embedded images**, like `Thumbnail` and `Preview` (if they exist) | ||
1. Robust **[type definitions](#tags)** of the top 99.5% tags used by over 6,000 | ||
1. Support for writing tags | ||
1. **[Type definitions](#tags)** of the top 99.5% tags used by over 6,000 | ||
different camera makes and models | ||
@@ -30,3 +32,3 @@ | ||
1. **Robust test suite**, performed with Node v4, v6, and v8 on [Linux, | ||
1. **Robust test coverage**, performed with the latest Node v4, v6, v8, and v9 on [Linux, | ||
Mac](https://travis-ci.org/mceachen/exiftool-vendored.js), & | ||
@@ -37,9 +39,12 @@ [Windows](https://ci.appveyor.com/project/mceachen/exiftool-vendored/branch/master). | ||
```sh | ||
npm install --save exiftool-vendored | ||
``` | ||
yarn add exiftool-vendored | ||
Note that `exiftool-vendored` provides an installation of ExifTool relevant for your platform | ||
through | ||
or | ||
npm install --save exiftool-vendored | ||
Note that `exiftool-vendored` provides an installation of ExifTool relevant | ||
for your local platform through | ||
[optionalDependencies](https://docs.npmjs.com/files/package.json#optionaldependencies). | ||
You shouldn't include either the `exiftool-vendored.exe` or | ||
@@ -50,43 +55,100 @@ `exiftool-vendored.pl` as direct dependencies to your project. | ||
There are many configuration options to ExifTool, but all values have defaults. | ||
Please review the [ExifTool constructor parameters](src/ExifTool.ts#L70) | ||
and override default values where appropriate. | ||
```js | ||
import { ExifTool } from "exiftool-vendored"; | ||
// Note that there are many configuration options to ExifTool. | ||
// Based on your hardware performance and expected performance | ||
// characteristics, the defaults may not be relevant for you. | ||
// See src/ExifTool.ts#L70 for jsdocs. | ||
const { ExifTool } = require("exiftool-vendored"); | ||
const exiftool = new ExifTool(); | ||
// Read all metadata tags in `path/to/image.jpg`. | ||
// Returns a `Promise<Tags>`. | ||
// And to verify everything is working: | ||
exiftool.version().then(version => | ||
console.log(`We're running ExifTool v${version}`) | ||
); | ||
``` | ||
### General API | ||
`ExifTool.read()` returns a Promise to a [Tags](src/Tags.ts) instance. Note | ||
that errors may be returned either by rejecting the promise, or for less | ||
severe problems, via the `errors` field. | ||
All other public ExifTool methods return `Promise<void>`, and will reject | ||
the promise if the operation is not successful. | ||
### Reading tags | ||
```js | ||
exiftool | ||
.read("path/to/image.jpg") | ||
.then((tags /*: Tags */) => console.log(`Make: ${tags.Make}, Model: ${tags.Model}`)) | ||
.then((tags /*: Tags */) => console.log(`Make: ${tags.Make}, Model: ${tags.Model}, Errors: ${tags.errors}`)) | ||
.catch(err => console.error("Something terrible happened: ", err)) | ||
``` | ||
// Extract the low-resolution thumbnail in `path/to/image.jpg`, | ||
// write it to `path/to/thumbnail.jpg`, and return a Promise<void> | ||
// that is fulfilled when the image is extracted: | ||
### Extracting embedded images | ||
Extract the low-resolution thumbnail in `path/to/image.jpg`, write it to | ||
`path/to/thumbnail.jpg`, and return a `Promise<void>` that is fulfilled | ||
when the image is extracted: | ||
```js | ||
exiftool.extractThumbnail("path/to/image.jpg", "path/to/thumbnail.jpg"); | ||
``` | ||
// Extract the "Preview" image (found in some images): | ||
Extract the `Preview` image (only found in some images): | ||
```js | ||
exiftool.extractPreview("path/to/image.jpg", "path/to/preview.jpg"); | ||
``` | ||
// Extract the "JpgFromRaw" image (found in some RAW images): | ||
Extract the `JpgFromRaw` image (found in some RAW images): | ||
```js | ||
exiftool.extractJpgFromRaw("path/to/image.cr2", "path/to/fromRaw.jpg"); | ||
``` | ||
// Extract the binary value from "tagname" tag in `path/to/image.jpg` | ||
// and write it to `dest.bin` (which cannot exist already | ||
// and whose parent directory must already exist): | ||
Extract the binary value from "tagname" tag in `path/to/image.jpg` | ||
and write it to `dest.bin` (which cannot exist already | ||
and whose parent directory must already exist): | ||
```js | ||
exiftool.extractBinaryTag("tagname", "path/to/file.exf", "path/to/dest.bin"); | ||
``` | ||
// Write a comment to the given file so it shows up in the | ||
// Windows Explorer Properties panel: | ||
### Writing tags | ||
Note that only a portion of tags are writable. Refer to [the | ||
documentation](https://sno.phy.queensu.ca/~phil/exiftool/TagNames/index.html) | ||
and look under the "Writable" column. | ||
If you apply malformed values or ask to write to tags that aren't | ||
supported, the returned `Promise` will be rejected. | ||
Only string and numeric primitive are supported as values to the object | ||
Write a comment to the given file so it shows up in the Windows Explorer | ||
Properties panel: | ||
```js | ||
exiftool.write("path/to/file.jpg", { XPComment: "this is a test comment" }); | ||
``` | ||
// Make sure you end ExifTool when you're done with it. | ||
// Note that `.end` returns a Promise that you can `await`. | ||
exiftool.end() | ||
Change the DateTimeOriginal, CreateDate and ModifyDate tags (using the | ||
[AllDates](https://sno.phy.queensu.ca/~phil/exiftool/TagNames/Shortcuts.html) | ||
shortcut) to 4:56pm UTC on February 6, 2016: | ||
```js | ||
exiftool.write("path/to/file.jpg", { AllDates: "2016-02-06T16:56:00" }); | ||
``` | ||
### Always Beware: Timezones | ||
If you edit a timestamp tag, realize that the difference between the | ||
changed timestamp tag and the GPS value is used by `exiftool-vendored` to | ||
infer the timezone. | ||
In other words, if you only edit the `CreateDate` and don't edit the `GPS` | ||
timestamps, your timezone will either be incorrect or missing. | ||
## Resource hygene | ||
@@ -96,7 +158,13 @@ | ||
ExifTool processes consume system resources. If you're done with it, turn | ||
it off with `.end()`, which returns a `Promise<void>` if you want to wait | ||
for the shutdown to be complete. | ||
### Mocha v4.0.0 | ||
If you use [mocha](https://mochajs.org/) v4 or later, and you don't call | ||
`exiftool.end()`, you will find that your tests hang after completion. [The relevant change is described | ||
here](https://github.com/mochajs/mocha/issues/3044), and can be solved by | ||
adding an `after` block that shuts down the instance of ExifTool that your | ||
tests are using: | ||
`exiftool.end()`, you will find that your test suite hangs. [The relevant | ||
change is described here](https://github.com/mochajs/mocha/issues/3044), | ||
and can be solved by adding an `after` block that shuts down the instance | ||
of ExifTool that your tests are using: | ||
@@ -161,3 +229,3 @@ ```js | ||
Using the `exiftool` npm package takes 7x longer: | ||
Using the `exiftool` npm package takes 7x longer (and doesn't work on Windows): | ||
@@ -174,10 +242,11 @@ ```sh | ||
On Windows, for **every invocation**, exiftool *installs a distribution of Perl | ||
**and** extracts the ~1000 files that make up ExifTool*, and then runs the | ||
script. Windows virus scanners prevent reads on these files while they are | ||
scanned, which makes this approach even more costly. | ||
On Windows, for **every invocation**, `exiftool` *installs a distribution | ||
of Perl **and** extracts the ~1000 files that make up ExifTool*, and | ||
**then** runs the perl script. Windows virus scanners prevent reads on | ||
these files while they are scanned, which makes this approach even more | ||
costly. | ||
Using ExifTool's `-stay_open` batch mode, we can reuse a single instance of | ||
ExifTool across many requests, dropping response latency dramatically and | ||
reducing system load. | ||
Using ExifTool's `-stay_open` batch mode means we can reuse a single | ||
instance of ExifTool across many requests, dropping response latency | ||
dramatically as well as reducing system load. | ||
@@ -184,0 +253,0 @@ ### Parallelism |
293456
5998
266