New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

exiftool-vendored

Package Overview
Dependencies
Maintainers
1
Versions
252
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

exiftool-vendored - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

17

lib/datetime.d.ts

@@ -7,2 +7,5 @@ export declare function compact<T>(array: T[]): T[];

}
/**
* Encodes an ExifTime (which may not have a timezone offset)
*/
export declare class ExifTime {

@@ -18,2 +21,5 @@ readonly hour: number;

}
/**
* Encodes an ExifDate
*/
export declare class ExifDate {

@@ -28,2 +34,5 @@ readonly year: number;

}
/**
* Encodes an ExifDateTime.
*/
export declare class ExifDateTime extends Base {

@@ -38,4 +47,12 @@ readonly year: number;

static regex: RegExp;
/**
* Note that this may have fractional precision (123.456ms)
*/
readonly millis: number;
constructor(year: number, month: number, day: number, hour: number, minute: number, second: number, secondFraction?: number, tzoffsetMinutes?: number);
/**
* Note that this is most likely incorrect if the timezone offset is not set.
*
* See the README for details.
*/
toDate(): Date;

@@ -42,0 +59,0 @@ toISOString(): string;

41

lib/datetime.js

@@ -43,4 +43,11 @@ "use strict";

exports.Base = Base;
/**
* Encodes an ExifTime (which may not have a timezone offset)
*/
class ExifTime {
constructor(hour, minute, second, secondFraction, tzoffsetMinutes) {
constructor(hour, // 1-23
minute, // 0-59
second, // 0-59
secondFraction, // 0-999
tzoffsetMinutes) {
this.hour = hour;

@@ -58,8 +65,13 @@ this.minute = minute;

exports.ExifTime = ExifTime;
/**
* Encodes an ExifDate
*/
class ExifDate {
constructor(year, month, day) {
constructor(year, // four-digit year
month, // 1-12, (no crazy 0-11 nonsense from Date!)
day) {
this.year = year;
this.month = month;
this.day = day;
}
} // tslint:disable-line
toString() {

@@ -74,4 +86,13 @@ return `${this.year}-${pad2(this.month)}-${pad2(this.day)}`;

exports.ExifDate = ExifDate;
/**
* Encodes an ExifDateTime.
*/
class ExifDateTime extends Base {
constructor(year, month, day, hour, minute, second, secondFraction, tzoffsetMinutes) {
constructor(year, month, // 1-12, no crazy 0-11 nonsense
day, // 1-31
hour, // 1-23
minute, // 0-59
second, // 0-59
secondFraction, // 0-999
tzoffsetMinutes) {
super();

@@ -87,2 +108,7 @@ this.year = year;

}
/**
* Note that this is most likely incorrect if the timezone offset is not set.
*
* See the README for details.
*/
toDate() {

@@ -96,2 +122,3 @@ if (this.tzoffsetMinutes === undefined) {

else if (this.tzoffsetMinutes === 0) {
// Don't leave it up to string parsing
return new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millis));

@@ -108,2 +135,3 @@ }

}
// The timezone offset will be extricated prior to this regex:
ExifDateTime.regex = /^(\d{4})[ :]+(\d{2})[ :]+(\d{2})[ :]+(\d{2})[ :]+(\d{2})[ :]+(\d{2})(?:\.(\d{1,6}))?$/;

@@ -161,2 +189,3 @@ exports.ExifDateTime = ExifDateTime;

exports.ExifTimeZoneOffset = ExifTimeZoneOffset;
// workaround for the fact that the spread operator doesn't work for constructors (!!?):
const newDateTime = _new(ExifDateTime.regex, (a) => {

@@ -171,3 +200,3 @@ return new ExifDateTime(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);

});
const emptyRe = /^[\s:]*$/;
const emptyRe = /^[\s:0]*$/; // Some empty datetimes come back as " : : "
function parse(tagName, rawTagValue, globalTzOffset) {

@@ -178,2 +207,3 @@ if (rawTagValue === undefined || emptyRe.exec(rawTagValue)) {

const tz = new ExifTimeZoneOffset(tagName, rawTagValue);
// If it's just a timezone:
if (tz.tzOffsetMinutes !== undefined && emptyRe.exec(tz.inputWithoutTimezone)) {

@@ -190,2 +220,1 @@ return tz;

exports.parse = parse;
//# sourceMappingURL=datetime.js.map

10

lib/datetime.spec.js

@@ -47,3 +47,3 @@ "use strict";

describe('example strings with tz', () => {
const dt = _dt.parse('DateTimeOriginal', '2013:12:30 11:04:15-05:00');
const dt = _dt.parse('DateTimeOriginal', '2013:12:30 11:04:15-05:00'); // non-local offset
it('year/month/day', () => {

@@ -139,2 +139,8 @@ chai_1.expect([dt.year, dt.month, dt.day]).to.eql([2013, 12, 30]);

});
//# sourceMappingURL=datetime.spec.js.map
describe('parsing empty/invalid input', () => {
['', ' ', '0000:00:00 00:00:00', ' : : : : '].forEach(bad => {
it(bad, () => {
chai_1.expect(_dt.parse('DateTimeOriginal', bad)).to.eql(bad);
});
});
});

@@ -23,2 +23,1 @@ "use strict";

exports.Deferred = Deferred;
//# sourceMappingURL=deferred.js.map

@@ -6,2 +6,5 @@ import { Task } from './task';

export declare function ellipsize(str: string, max: number): string;
/**
* Manages a child process. Callers need to restart if ended.
*/
export declare class ExifToolProcess {

@@ -17,3 +20,9 @@ private readonly taskProvider;

end(): void;
/**
* @return true if `end()` was called, or the child process has closed
*/
readonly ended: boolean;
/**
* @return true if the child process has closed
*/
readonly closed: boolean;

@@ -20,0 +29,0 @@ readonly closedPromise: Promise<void>;

@@ -17,2 +17,5 @@ "use strict";

exports.ellipsize = ellipsize;
/**
* Manages a child process. Callers need to restart if ended.
*/
class ExifToolProcess {

@@ -25,6 +28,6 @@ constructor(taskProvider) {

this.proc = cp.spawn(exiftoolPath, ['-stay_open', 'True', '-@', '-']);
this.proc.unref();
this.proc.unref(); // don't let node count ExifTool as a reason to stay alive
this.proc.stdout.on('data', d => this.onData(d));
this.proc.stderr.on('data', d => this.onError(d));
this.proc.on('close', (code) => {
this.proc.on('close', () => {
this._ended = true;

@@ -43,5 +46,11 @@ this._closedDeferred.resolve();

}
/**
* @return true if `end()` was called, or the child process has closed
*/
get ended() {
return this._ended;
}
/**
* @return true if the child process has closed
*/
get closed() {

@@ -64,3 +73,3 @@ return this._closedDeferred.fulfilled;

'-execute',
''
'' // Need to end -execute with a newline
].join('\n');

@@ -90,3 +99,3 @@ this.proc.stdin.write(cmd);

this.currentTask = undefined;
this.workIfIdle();
this.workIfIdle(); // start the next job running before parsing this one to minimize latency
if (task === undefined) {

@@ -106,2 +115,1 @@ if (buff.length > 0) {

exports.ExifToolProcess = ExifToolProcess;
//# sourceMappingURL=exiftool_process.js.map

@@ -15,2 +15,1 @@ "use strict";

});
//# sourceMappingURL=exiftool_process.spec.js.map

@@ -7,4 +7,16 @@ /// <reference types="node" />

export interface ExifToolAPI {
/**
* @return a promise holding the version number of the vendored ExifTool
*/
version(): Promise<string>;
/**
* @return a Promise holding the metadata tags found in `file`.
*/
read(file: string): Promise<Tags>;
/**
* Request graceful shut down of any running ExifTool child processes.
*
* This may need to be called in `after` or `finally` clauses in tests
* or scripts for them to exit cleanly.
*/
end(): void;

@@ -17,4 +29,16 @@ }

}
/**
* Assign your custom logger to this instance.
*/
export declare let logger: Console;
/**
* This is the version of the `exiftool-vendored` npm module.
* The package.json value is made to match this value by `npm run update`.
*/
export declare const ExifToolVendoredVersion: string;
/**
* Manages delegating calls to a vendored running instance of ExifTool.
*
* Instantiation is expensive: use the exported singleton instance of this class, `exiftool`.
*/
export declare class ExifTool implements ExifToolAPI {

@@ -24,6 +48,21 @@ readonly maxProcs: number;

private _tasks;
/**
* @param maxProcs the maximum number of ExifTool child processes to spawn when load merits
*/
constructor(maxProcs?: number);
/**
* @return a Promise to the vendored ExifTool's version
*/
version(): Promise<string>;
/**
* @return a Promise holding the metadata tags found in `file`.
*/
read(file: string): Promise<Tags>;
end(): Promise<void>;
/**
* Request graceful shut down of any running ExifTool child processes.
*
* This may need to be called in `after` or `finally` clauses in tests
* or scripts for them to exit cleanly.
*/
end(): Promise<any>;
enqueueTask<T>(task: Task<T>): Task<T>;

@@ -34,2 +73,6 @@ private dequeueTask();

}
/**
* Use this singleton rather than instantiating new ExifTool instances
* in order to leverage a single running ExifTool process.
*/
export declare const exiftool: ExifToolAPI;

@@ -10,5 +10,20 @@ "use strict";

exports.ExifTimeZoneOffset = datetime_1.ExifTimeZoneOffset;
/**
* Assign your custom logger to this instance.
*/
exports.logger = console;
exports.ExifToolVendoredVersion = '0.3.0';
/**
* This is the version of the `exiftool-vendored` npm module.
* The package.json value is made to match this value by `npm run update`.
*/
exports.ExifToolVendoredVersion = '0.4.0';
/**
* Manages delegating calls to a vendored running instance of ExifTool.
*
* Instantiation is expensive: use the exported singleton instance of this class, `exiftool`.
*/
class ExifTool {
/**
* @param maxProcs the maximum number of ExifTool child processes to spawn when load merits
*/
constructor(maxProcs = 1) {

@@ -18,9 +33,21 @@ this.maxProcs = maxProcs;

this._tasks = [];
}
} // tslint:disable-line
/**
* @return a Promise to the vendored ExifTool's version
*/
version() {
return this.enqueueTask(new version_task_1.VersionTask()).promise;
}
/**
* @return a Promise holding the metadata tags found in `file`.
*/
read(file) {
return this.enqueueTask(tags_task_1.TagsTask.for(file)).promise;
}
/**
* Request graceful shut down of any running ExifTool child processes.
*
* This may need to be called in `after` or `finally` clauses in tests
* or scripts for them to exit cleanly.
*/
end() {

@@ -52,3 +79,6 @@ this._procs.forEach(p => p.end());

exports.ExifTool = ExifTool;
/**
* Use this singleton rather than instantiating new ExifTool instances
* in order to leverage a single running ExifTool process.
*/
exports.exiftool = new ExifTool();
//# sourceMappingURL=exiftool.js.map

@@ -29,3 +29,3 @@ "use strict";

function normalize(tagNames) {
return tagNames.filter(i => i !== "FileInodeChangeDate" && i !== 'FileCreateDate').sort();
return tagNames.filter(i => i !== 'FileInodeChangeDate' && i !== 'FileCreateDate').sort();
}

@@ -62,2 +62,1 @@ it('returns no exif metadata for an image with no headers', () => {

});
//# sourceMappingURL=exiftool.spec.js.map
"use strict";
//# sourceMappingURL=parser.js.map

@@ -25,4 +25,7 @@ "use strict";

this.rawTags = JSON.parse(data)[0];
// ExifTool does humorous things to paths, like flip slashes. resolve() undoes that.
const SourceFile = _path.resolve(this.rawTags.SourceFile);
// Sanity check that the result is for the file we want:
if (SourceFile !== this.SourceFile) {
// Throw an error rather than add an errors string because this is *really* bad:
throw new Error(`Internal error: unexpected SourceFile of ${this.rawTags.SourceFile} for file ${this.SourceFile}`);

@@ -36,2 +39,3 @@ }

extractTzoffset() {
// TimeZone just wins if we're just handed it, then use it:
const tze = new _dt.ExifTimeZoneOffset('TimeZone', this.rawTags.TimeZone);

@@ -45,2 +49,3 @@ if (tze.tzOffsetMinutes !== undefined) {

if (gps && local) {
// timezone offsets are never less than 30 minutes.
const gpsToHalfHour = gps.toDate().getTime() / (30 * 60 * 1000);

@@ -63,3 +68,3 @@ const localToHalfHour = local.toDate().getTime() / (30 * 60 * 1000);

|| tagName.endsWith('Firmware') || tagName.endsWith('DateDisplayFormat')) {
return value.toString();
return value.toString(); // force to string
}

@@ -79,3 +84,3 @@ else if (tagName.endsWith('BitsPerSample')) {

if (ref === undefined) {
return value;
return value; // give up
}

@@ -99,2 +104,1 @@ else {

exports.TagsTask = TagsTask;
//# sourceMappingURL=tags_task.js.map

@@ -14,2 +14,11 @@ "use strict";

describe('Lat/Lon parsing', () => {
/* Example:
$ exiftool -j -coordFormat '%.8f' -fast ../test-images/important/Apple_iPhone7Plus.jpg | grep itude
"GPSLatitudeRef": "North",
"GPSLongitudeRef": "East",
"GPSAltitudeRef": "Above Sea Level",
"GPSAltitude": "73 m Above Sea Level",
"GPSLatitude": "22.33543889 N",
"GPSLongitude": "114.16401667 E",
*/
it('N lat is positive', () => {

@@ -82,2 +91,1 @@ chai_1.expect(parse({ GPSLatitude: '22.33543889 N' }).GPSLatitude).to.be.closeTo(22.33543889, 0.00001);

});
//# sourceMappingURL=tags_task.spec.js.map
"use strict";
//# sourceMappingURL=tags.js.map
import { Deferred } from './deferred';
/**
* Emodies both a command (`args`), and a handler for the resulting output
*/
export declare abstract class Task<T> extends Deferred<T> {

@@ -3,0 +6,0 @@ readonly args: string[];

"use strict";
const deferred_1 = require('./deferred');
/**
* Emodies both a command (`args`), and a handler for the resulting output
*/
class Task extends deferred_1.Deferred {

@@ -18,2 +21,1 @@ constructor(args) {

exports.Task = Task;
//# sourceMappingURL=task.js.map

@@ -0,2 +1,17 @@

/**
* If the vendored ExifTool version is different from
* the current latest version, download and extract
* into the appropriate sibling directories, and update
* their package.json.
*
* Running tests, committing to git, and publishing the npm are
* manual subsequent steps.
*/
export declare class Checksums {
/**
* @param checksums is a newline-separated text file with the following format:
* $HASHTYPE($FILENAME) = $HASH
*
* We only care about SHA1, so we're pulling just those out.
*/
private static readonly regex;

@@ -3,0 +18,0 @@ private readonly store;

"use strict";
const io_1 = require('./io');
/**
* If the vendored ExifTool version is different from
* the current latest version, download and extract
* into the appropriate sibling directories, and update
* their package.json.
*
* Running tests, committing to git, and publishing the npm are
* manual subsequent steps.
*/
class Checksums {

@@ -21,4 +30,9 @@ constructor(checksums) {

}
/**
* @param checksums is a newline-separated text file with the following format:
* $HASHTYPE($FILENAME) = $HASH
*
* We only care about SHA1, so we're pulling just those out.
*/
Checksums.regex = /SHA1 ?\((\S+)\) ?= ?([a-f0-9]+)/i;
exports.Checksums = Checksums;
//# sourceMappingURL=checksums.js.map

@@ -14,3 +14,3 @@ "use strict";

this.version = version;
}
} // tslint:disable-line
static parsedPath(url) {

@@ -57,4 +57,4 @@ const parsedUrlPathname = _url.parse(url).pathname;

}
// The file suffix and the path will already be stripped out at this point
Enclosure.regex = /.*?([\d\.]+)(\.tar\.gz|\.zip)$/;
exports.Enclosure = Enclosure;
//# sourceMappingURL=enclosure.js.map

@@ -194,2 +194,1 @@ "use strict";

exports.mkdir = mkdir;
//# sourceMappingURL=io.js.map

@@ -9,2 +9,3 @@ "use strict";

const _path = require('path');
// ☠☠ THIS IS PRETTY GRISLY CODE. SCROLL DOWN AT YOUR OWN PERIL ☠☠
const globule = require('globule');

@@ -56,6 +57,7 @@ function usage() {

this.values = [];
}
} // tslint:disable-line
get group() { return this.tag.split(':')[0]; }
get withoutGroup() { return this.tag.split(':')[1]; }
get valueType() {
// hard-coded because the ☆☆☆ ITPC tag doesn't match the ★★★ Composite tag, causing Tags not to compile
if (this.withoutGroup === 'DateCreated') {

@@ -67,2 +69,3 @@ return 'ExifDate';

const byCount = cm.byCountDesc().slice(0, 2);
// If an "Exif*" type is common, let's use that instead of string.
if (byCount[0] === 'string' && ('' + byCount[1]).startsWith('Exif')) {

@@ -135,3 +138,3 @@ byCount.shift();

const saneTagRe = /^[a-z0-9_]+:[a-z0-9_]+$/i;
const exiftool = new exiftool_1.ExifTool(4);
const exiftool = new exiftool_1.ExifTool(4); // so I can use `enqueueTask`
const start = Date.now();

@@ -159,3 +162,3 @@ Promise.all(files.map(file => {

const tagWriter = _fs.createWriteStream(destFile);
tagWriter.write('/* tslint:disable:class-name */\n');
tagWriter.write('/* tslint:disable:class-name */\n'); // because of ICC_Profile
tagWriter.write(`import { ExifDate, ExifTime, ExifDateTime, ExifTimeZoneOffset } from './datetime'\n\n`);

@@ -184,3 +187,2 @@ tagWriter.write(`// Autogenerated by "npm run mktags" by ExifTool ${version} on ${new Date().toDateString()}.\n\n`);

tagWriter.write('}\n');
tagWriter.write('\n');
tagWriter.end();

@@ -192,2 +194,1 @@ }).catch(err => {

});
//# sourceMappingURL=mktags.js.map

@@ -19,3 +19,4 @@ "use strict";

downloadMaybeAndVerify() {
return this.verify().catch(err => io.rmrf(this.dlDest, true)
// tslint:disable-next-line: handle-callback-err
return this.verify().catch(() => io.rmrf(this.dlDest, true)
.then(() => this.download())

@@ -70,2 +71,3 @@ .then(() => this.verify()));

.then(() => {
// The tarball is prefixed with "Image-ExifTool-VERSION". Move that subdirectory into bin proper.
const subdir = globule.find(_path.join(tmpUnpack, `Image-ExifTool*${_path.sep}`));

@@ -115,2 +117,1 @@ if (subdir.length !== 1) {

update();
//# sourceMappingURL=update.js.map

@@ -19,2 +19,1 @@ "use strict";

exports.VersionTask = VersionTask;
//# sourceMappingURL=version_task.js.map
{
"name": "exiftool-vendored",
"version": "0.3.0",
"version": "0.4.0",
"description": "Efficient, cross-platform access to ExifTool",

@@ -18,2 +18,3 @@ "main": "./lib/exiftool.js",

"cleantest": "run-s clean lint fmt pdm test",
"preversion": "run-s lint pdm",
"pdm": "platform-dependent-modules",

@@ -79,8 +80,10 @@ "postinstall": "npm run pdm"

"mocha": "^3.1.2",
"np": "^2.9.0",
"npm-run-all": "^3.1.1",
"rimraf": "^2.5.4",
"tar-fs": "^1.14.0",
"tslint": "^3.15.1",
"tslint-config-standard": "^1.5.0",
"typescript": "^2.0.3",
"typescript-formatter": "^3.1.0",
"typescript": "^2.0.8",
"typescript-formatter": "^4.0.0",
"xmldom": "^0.1.22",

@@ -87,0 +90,0 @@ "xpath": "0.0.23"

@@ -11,4 +11,6 @@ # exiftool-vendored

1. **High performance** via [`-stay_open`](#stay_open) and [multithreading](#parallelism), with [7-300x faster](#performance) than competing packages
1. **High performance** via [`-stay_open`](#stay_open) and [multithreading](#parallelism). [7-300x faster](#performance) than competing packages
1. Support for [Mac, Linux](https://travis-ci.org/mceachen/exiftool-vendored), and [Windows](https://ci.appveyor.com/project/mceachen/exiftool-vendored/branch/master).
1. Proper extraction of

@@ -115,2 +117,7 @@ - **dates** with [correct timezone offset encoding, when available](#dates))

### v0.4.0
* Fixed packaging (maintain jsdocs in .d.ts)
* Using [np](https://www.npmjs.com/package/np) for packaging
### v0.3.0

@@ -117,0 +124,0 @@

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc