Initial commit
This commit is contained in:
commit
e49726988c
5445 files changed
+1089914
No files matched your search
+5
@@ -0,0 +1,5 @@
|
||||
import { IPTCApplicationRecordTags } from "./IPTCApplicationRecordTags";
|
||||
/**
|
||||
* @deprecated Use {@link IPTCApplicationRecordTags} instead
|
||||
*/
|
||||
export type ApplicationRecordTags = IPTCApplicationRecordTags;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=ApplicationRecordTags.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ApplicationRecordTags.js","sourceRoot":"","sources":["../src/ApplicationRecordTags.ts"],"names":[],"mappings":""}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { Maybe, Nullable } from "./Maybe";
|
||||
export declare function isIterable(obj: unknown): obj is Iterable<unknown>;
|
||||
export declare function ifArray<T = unknown>(arr: T[] | unknown): Maybe<T[]>;
|
||||
export declare function toArray<T>(arr: undefined | null | T[] | T | Iterable<T>): T[];
|
||||
export declare function compact<T>(array: Nullable<T>[]): T[];
|
||||
/**
|
||||
* Remove all elements from the given array that return false from the given
|
||||
* predicate `filter`.
|
||||
*/
|
||||
export declare function filterInPlace<T>(arr: T[], filter: (t: T) => boolean): T[];
|
||||
export declare function uniq<T>(arr: T[]): T[];
|
||||
export declare function shallowArrayEql(a: unknown[], b: unknown[]): boolean;
|
||||
type Comparable = number | string | boolean;
|
||||
/**
|
||||
* Returns a copy of arr, stable sorted by the given constraint. Note that false
|
||||
* < true, and that `f` may return an array for sort priorities, or undefined if
|
||||
* the item should be skipped from the returned result.
|
||||
*
|
||||
* Note: localeSort() thinks lower case should come before upper case (!!)
|
||||
*/
|
||||
export declare function sortBy<T>(arr: Iterable<Maybe<T>> | Maybe<T>[], f: (t: T) => Maybe<Comparable>): T[];
|
||||
export declare function leastBy<T>(haystack: T[], f: (t: T) => Maybe<Comparable>): Maybe<T>;
|
||||
export {};
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isIterable = isIterable;
|
||||
exports.ifArray = ifArray;
|
||||
exports.toArray = toArray;
|
||||
exports.compact = compact;
|
||||
exports.filterInPlace = filterInPlace;
|
||||
exports.uniq = uniq;
|
||||
exports.shallowArrayEql = shallowArrayEql;
|
||||
exports.sortBy = sortBy;
|
||||
exports.leastBy = leastBy;
|
||||
const Object_1 = require("./Object");
|
||||
const String_1 = require("./String");
|
||||
function isIterable(obj) {
|
||||
return ((0, Object_1.isObject)(obj) && Symbol.iterator in obj) || Array.isArray(obj);
|
||||
}
|
||||
function ifArray(arr) {
|
||||
return Array.isArray(arr) ? arr : undefined;
|
||||
}
|
||||
function toArray(arr) {
|
||||
return Array.isArray(arr) // < strings are not arrays
|
||||
? arr
|
||||
: arr == null
|
||||
? []
|
||||
: (0, String_1.isString)(arr) // < don't rely on isIterable rejecting Strings
|
||||
? [arr]
|
||||
: isIterable(arr)
|
||||
? Array.from(arr)
|
||||
: [arr];
|
||||
}
|
||||
function compact(array) {
|
||||
return array.filter((elem) => elem != null);
|
||||
}
|
||||
/**
|
||||
* Remove all elements from the given array that return false from the given
|
||||
* predicate `filter`.
|
||||
*/
|
||||
function filterInPlace(arr, filter) {
|
||||
let j = 0;
|
||||
arr.forEach((ea, i) => {
|
||||
if (filter(ea)) {
|
||||
if (i !== j)
|
||||
arr[j] = ea;
|
||||
j++;
|
||||
}
|
||||
});
|
||||
arr.length = j;
|
||||
return arr;
|
||||
}
|
||||
function uniq(arr) {
|
||||
return arr.reduce((acc, ea) => {
|
||||
if (acc.indexOf(ea) === -1)
|
||||
acc.push(ea);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
// terrible implementation only for internal use
|
||||
function shallowArrayEql(a, b) {
|
||||
return (a != null &&
|
||||
b != null &&
|
||||
a.length === b.length &&
|
||||
a.every((ea, idx) => ea === b[idx]));
|
||||
}
|
||||
/**
|
||||
* Returns a copy of arr, stable sorted by the given constraint. Note that false
|
||||
* < true, and that `f` may return an array for sort priorities, or undefined if
|
||||
* the item should be skipped from the returned result.
|
||||
*
|
||||
* Note: localeSort() thinks lower case should come before upper case (!!)
|
||||
*/
|
||||
function sortBy(arr, f) {
|
||||
return toArray(arr).filter((ea) => ea != null)
|
||||
.map((item) => ({
|
||||
item,
|
||||
cmp: f(item),
|
||||
}))
|
||||
.filter((ea) => ea.cmp != null)
|
||||
.sort((a, b) => cmp(a.cmp, b.cmp))
|
||||
.map((ea) => ea.item);
|
||||
}
|
||||
function cmp(a, b) {
|
||||
// undefined == undefined:
|
||||
if (a == null && b == null)
|
||||
return 0;
|
||||
// undefined should be < defined. We can't use typeof here because typeof null
|
||||
// is "object" and typeof undefined = "undefined".
|
||||
if (a == null)
|
||||
return -1;
|
||||
if (b == null)
|
||||
return 1;
|
||||
const aType = typeof a;
|
||||
const bType = typeof b;
|
||||
if ((aType === "string" || aType === "symbol") &&
|
||||
(bType === "string" || bType === "symbol")) {
|
||||
// in German, ä sorts before z, in Swedish, ä sorts after z
|
||||
return String(a).localeCompare(String(b));
|
||||
}
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
}
|
||||
function leastBy(haystack, f) {
|
||||
let min;
|
||||
let result;
|
||||
for (const ea of haystack) {
|
||||
const val = f(ea);
|
||||
if (val != null && (min == null || val < min)) {
|
||||
min = val;
|
||||
result = ea;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//# sourceMappingURL=Array.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Array.js","sourceRoot":"","sources":["../src/Array.ts"],"names":[],"mappings":"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"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare function retryOnReject<T>(f: () => T | Promise<T>, maxRetries: number): Promise<T>;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.retryOnReject = retryOnReject;
|
||||
function retryOnReject(f, maxRetries) {
|
||||
let retries = 0;
|
||||
const g = async () => {
|
||||
try {
|
||||
return await f();
|
||||
}
|
||||
catch (err) {
|
||||
if (retries < maxRetries) {
|
||||
retries++;
|
||||
return g();
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
return g();
|
||||
}
|
||||
//# sourceMappingURL=AsyncRetry.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"AsyncRetry.js","sourceRoot":"","sources":["../src/AsyncRetry.ts"],"names":[],"mappings":";;AAAA,sCAkBC;AAlBD,SAAgB,aAAa,CAC3B,CAAuB,EACvB,UAAkB;IAElB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,CAAC,GAAqB,KAAK,IAAI,EAAE;QACrC,IAAI,CAAC;YACH,OAAO,MAAM,CAAC,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,EAAE,CAAC;YACb,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC;AACb,CAAC"}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { ExifToolOptions } from "./ExifToolOptions";
|
||||
import { ExifToolTask } from "./ExifToolTask";
|
||||
import { Maybe } from "./Maybe";
|
||||
export type BinaryExtractionTaskOptions = Pick<ExifToolOptions, "ignoreMinorErrors"> & Partial<Pick<ExifToolOptions, "forceWrite">>;
|
||||
/**
|
||||
* Task that returns an error string (to prevent retries), or undefined if
|
||||
* everything seems to have worked.
|
||||
*/
|
||||
export declare class BinaryExtractionTask extends ExifToolTask<Maybe<string>> {
|
||||
private constructor();
|
||||
static for(tagname: string, imgSrc: string, imgDest: string, options?: BinaryExtractionTaskOptions): BinaryExtractionTask;
|
||||
parse(stdout: string, err?: Error): Maybe<string>;
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BinaryExtractionTask = void 0;
|
||||
const node_fs_1 = require("node:fs");
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const ExifToolTask_1 = require("./ExifToolTask");
|
||||
const FilenameCharsetArgs_1 = require("./FilenameCharsetArgs");
|
||||
const String_1 = require("./String");
|
||||
const StdoutRe = /\b(\d+) output files? created/i;
|
||||
/**
|
||||
* Task that returns an error string (to prevent retries), or undefined if
|
||||
* everything seems to have worked.
|
||||
*/
|
||||
class BinaryExtractionTask extends ExifToolTask_1.ExifToolTask {
|
||||
constructor(args, options) {
|
||||
super(args, options);
|
||||
}
|
||||
static for(tagname, imgSrc, imgDest, options) {
|
||||
// Ensure the destination directory exists:
|
||||
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(imgDest), { recursive: true });
|
||||
const forceWrite = options?.forceWrite ?? false;
|
||||
const args = [
|
||||
...FilenameCharsetArgs_1.Utf8FilenameCharsetArgs,
|
||||
"-b", // -binary
|
||||
"-" + tagname,
|
||||
// Capital W allows destination files to not have a pattern. See
|
||||
// https://exiftool.org/exiftool_pod.html#W-FMT--tagOut
|
||||
//
|
||||
// Prior code used -w with %0f "to avoid shell expansion", but as this
|
||||
// command gets sent directly to exiftool thanks to stay_open mode, we
|
||||
// don't need to worry about shell expansion.
|
||||
//
|
||||
// I also tried `-out` instead of `-W`, and that didn't work at least on
|
||||
// ExifTool 13.17.
|
||||
forceWrite ? "-W!" : "-W",
|
||||
node_path_1.default.resolve(imgDest),
|
||||
node_path_1.default.resolve(imgSrc),
|
||||
];
|
||||
return new BinaryExtractionTask(args, options);
|
||||
}
|
||||
parse(stdout, err) {
|
||||
const s = (0, String_1.toS)(stdout).trim();
|
||||
const m = StdoutRe.exec(s);
|
||||
if (err != null) {
|
||||
throw err;
|
||||
}
|
||||
else if (m == null) {
|
||||
throw new Error("Missing expected status message (got " + stdout + ")");
|
||||
}
|
||||
else if (m[1] === "1") {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Don't retry: the binary payload is missing, and retrying won't fix that.
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BinaryExtractionTask = BinaryExtractionTask;
|
||||
//# sourceMappingURL=BinaryExtractionTask.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BinaryExtractionTask.js","sourceRoot":"","sources":["../src/BinaryExtractionTask.ts"],"names":[],"mappings":";;;;;;AAAA,qCAAoC;AACpC,0DAA6B;AAE7B,iDAA8C;AAC9C,+DAAgE;AAEhE,qCAA+B;AAQ/B,MAAM,QAAQ,GAAG,gCAAgC,CAAC;AAElD;;;GAGG;AACH,MAAa,oBAAqB,SAAQ,2BAA2B;IACnE,YAAoB,IAAc,EAAE,OAAqC;QACvE,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,GAAG,CACR,OAAe,EACf,MAAc,EACd,OAAe,EACf,OAAqC;QAErC,2CAA2C;QAC3C,IAAA,mBAAS,EAAC,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEtD,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,KAAK,CAAC;QAEhD,MAAM,IAAI,GAAG;YACX,GAAG,6CAAuB;YAC1B,IAAI,EAAE,UAAU;YAChB,GAAG,GAAG,OAAO;YACb,gEAAgE;YAChE,uDAAuD;YACvD,EAAE;YACF,sEAAsE;YACtE,sEAAsE;YACtE,6CAA6C;YAC7C,EAAE;YACF,wEAAwE;YACxE,kBAAkB;YAClB,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YACzB,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YACrB,mBAAI,CAAC,OAAO,CAAC,MAAM,CAAC;SACrB,CAAC;QACF,OAAO,IAAI,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,MAAc,EAAE,GAAW;QAC/B,MAAM,CAAC,GAAG,IAAA,YAAG,EAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,MAAM,GAAG,CAAC;QACZ,CAAC;aAAM,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,uCAAuC,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC;QAC1E,CAAC;aAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;aAAM,CAAC;YACN,2EAA2E;YAC3E,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;CACF;AAlDD,oDAkDC"}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { Maybe } from "./Maybe";
|
||||
/**
|
||||
* Represents a binary field in the Exif data.
|
||||
*
|
||||
* These fields can be used to store images (including thumbnails and full-sized
|
||||
* images), audio, and other binary data. The size of the field is specified in
|
||||
* bytes.
|
||||
*/
|
||||
export declare class BinaryField {
|
||||
readonly bytes: number;
|
||||
readonly rawValue: string;
|
||||
constructor(bytes: number, rawValue: string);
|
||||
toJSON(): {
|
||||
_ctor: string;
|
||||
bytes: number;
|
||||
rawValue: string;
|
||||
};
|
||||
static fromJSON(json: ReturnType<BinaryField["toJSON"]>): BinaryField;
|
||||
static fromRawValue(rawValue: string): Maybe<BinaryField>;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BinaryField = void 0;
|
||||
const Number_1 = require("./Number");
|
||||
// "(Binary data 2506078 bytes, use -b option to extract)"
|
||||
const BinaryFieldRE =
|
||||
// 1000000000 bytes is 1 GB. The largest binary field I've seen is ~5 MB (7
|
||||
// chars): 10 chars is absurdly large, and is just to avoid the
|
||||
// `js/polynomial-redos` eslint rule.
|
||||
/Binary(?: data)? (\d{1,10}) bytes/i;
|
||||
/**
|
||||
* Represents a binary field in the Exif data.
|
||||
*
|
||||
* These fields can be used to store images (including thumbnails and full-sized
|
||||
* images), audio, and other binary data. The size of the field is specified in
|
||||
* bytes.
|
||||
*/
|
||||
class BinaryField {
|
||||
bytes;
|
||||
rawValue;
|
||||
constructor(bytes, rawValue) {
|
||||
this.bytes = bytes;
|
||||
this.rawValue = rawValue;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
_ctor: "BinaryField",
|
||||
bytes: this.bytes,
|
||||
rawValue: this.rawValue,
|
||||
};
|
||||
}
|
||||
static fromJSON(json) {
|
||||
return new BinaryField(json.bytes, json.rawValue);
|
||||
}
|
||||
static fromRawValue(rawValue) {
|
||||
const m = rawValue.match(BinaryFieldRE);
|
||||
if (m != null) {
|
||||
const bytes = (0, Number_1.toInt)(m[1]);
|
||||
if (bytes != null) {
|
||||
return new BinaryField(bytes, rawValue);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
exports.BinaryField = BinaryField;
|
||||
//# sourceMappingURL=BinaryField.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BinaryField.js","sourceRoot":"","sources":["../src/BinaryField.ts"],"names":[],"mappings":";;;AACA,qCAAiC;AAEjC,0DAA0D;AAC1D,MAAM,aAAa;AACjB,2EAA2E;AAC3E,+DAA+D;AAC/D,qCAAqC;AACrC,oCAAoC,CAAC;AAEvC;;;;;;GAMG;AACH,MAAa,WAAW;IAEX;IACA;IAFX,YACW,KAAa,EACb,QAAgB;QADhB,UAAK,GAAL,KAAK,CAAQ;QACb,aAAQ,GAAR,QAAQ,CAAQ;IACxB,CAAC;IAEJ,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,IAAuC;QACrD,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,QAAgB;QAClC,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,IAAA,cAAK,EAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBAClB,OAAO,IAAI,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,OAAO;IACT,CAAC;CACF;AA5BD,kCA4BC"}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { ExifToolTask, ExifToolTaskOptions } from "./ExifToolTask";
|
||||
/**
|
||||
* Task that returns an error string (to prevent retries), or undefined if
|
||||
* everything seems to have worked.
|
||||
*/
|
||||
export declare class BinaryToBufferTask extends ExifToolTask<Buffer | Error> {
|
||||
readonly tagname: string;
|
||||
private constructor();
|
||||
static for(tagname: string, imgSrc: string, options?: ExifToolTaskOptions): BinaryToBufferTask;
|
||||
parse(data: string, err?: Error): Buffer | Error;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BinaryToBufferTask = void 0;
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const ExifToolTask_1 = require("./ExifToolTask");
|
||||
const FilenameCharsetArgs_1 = require("./FilenameCharsetArgs");
|
||||
const String_1 = require("./String");
|
||||
/**
|
||||
* Task that returns an error string (to prevent retries), or undefined if
|
||||
* everything seems to have worked.
|
||||
*/
|
||||
class BinaryToBufferTask extends ExifToolTask_1.ExifToolTask {
|
||||
tagname;
|
||||
constructor(tagname, args, options) {
|
||||
super(args, options);
|
||||
this.tagname = tagname;
|
||||
}
|
||||
static for(tagname, imgSrc, options) {
|
||||
// NOTE TO FUTURE ME: we don't need to escape these arguments, because
|
||||
// ExifTool separates them via newlines.
|
||||
const args = [...FilenameCharsetArgs_1.Utf8FilenameCharsetArgs, "-json", "-b", "-" + tagname];
|
||||
args.push(node_path_1.default.resolve(imgSrc));
|
||||
return new BinaryToBufferTask(tagname, args, options);
|
||||
}
|
||||
parse(data, err) {
|
||||
try {
|
||||
const obj = JSON.parse(data)?.[0];
|
||||
// did they get the casing right?
|
||||
{
|
||||
const result = decode(obj[this.tagname]);
|
||||
if (result != null)
|
||||
return result;
|
||||
} // hmm, incorrect casing. Check the other keys.
|
||||
for (const k of Object.keys(obj)) {
|
||||
if (k.toLowerCase() === this.tagname.toLowerCase()) {
|
||||
const result = decode(obj[k]);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (caught) {
|
||||
err ??= (0, String_1.notBlank)(data)
|
||||
? new Error(data)
|
||||
: caught instanceof Error
|
||||
? caught
|
||||
: new Error(String(caught));
|
||||
}
|
||||
return err ?? new Error(this.tagname + " not found");
|
||||
}
|
||||
}
|
||||
exports.BinaryToBufferTask = BinaryToBufferTask;
|
||||
const B64Prefix = "base64:";
|
||||
function decode(data) {
|
||||
return data == null || !data.startsWith(B64Prefix)
|
||||
? undefined
|
||||
: Buffer.from(data.substring(B64Prefix.length), "base64");
|
||||
}
|
||||
//# sourceMappingURL=BinaryToBufferTask.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BinaryToBufferTask.js","sourceRoot":"","sources":["../src/BinaryToBufferTask.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,iDAAmE;AACnE,+DAAgE;AAEhE,qCAAoC;AAEpC;;;GAGG;AACH,MAAa,kBAAmB,SAAQ,2BAA4B;IAEvD;IADX,YACW,OAAe,EACxB,IAAc,EACd,OAA6B;QAE7B,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAJZ,YAAO,GAAP,OAAO,CAAQ;IAK1B,CAAC;IAED,MAAM,CAAC,GAAG,CACR,OAAe,EACf,MAAc,EACd,OAA6B;QAE7B,sEAAsE;QACtE,wCAAwC;QACxC,MAAM,IAAI,GAAG,CAAC,GAAG,6CAAuB,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;QACxE,IAAI,CAAC,IAAI,CAAC,mBAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAChC,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,IAAY,EAAE,GAAW;QAC7B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAClC,iCAAiC;YACjC,CAAC;gBACC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBACzC,IAAI,MAAM,IAAI,IAAI;oBAAE,OAAO,MAAM,CAAC;YACpC,CAAC,CAAC,+CAA+C;YACjD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;oBACnD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC9B,IAAI,MAAM,IAAI,IAAI;wBAAE,OAAO,MAAM,CAAC;gBACpC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,GAAG,KAAK,IAAA,iBAAQ,EAAC,IAAI,CAAC;gBACpB,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC;gBACjB,CAAC,CAAC,MAAM,YAAY,KAAK;oBACvB,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,CAAC;IACvD,CAAC;CACF;AA5CD,gDA4CC;AAED,MAAM,SAAS,GAAG,SAAS,CAAC;AAE5B,SAAS,MAAM,CAAC,IAAmB;IACjC,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAChD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC"}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
import { Maybe } from "./Maybe";
|
||||
export declare function toBoolean(value: unknown): Maybe<boolean>;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.toBoolean = toBoolean;
|
||||
const Truthy = ["true", "yes", "1", "on"];
|
||||
const Falsy = ["false", "no", "0", "off"];
|
||||
function toBoolean(value) {
|
||||
if (value == null)
|
||||
return undefined;
|
||||
if (typeof value === "boolean")
|
||||
return value;
|
||||
const s = String(value).trim().toLowerCase();
|
||||
return Truthy.includes(s) ? true : Falsy.includes(s) ? false : undefined;
|
||||
}
|
||||
//# sourceMappingURL=Boolean.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Boolean.js","sourceRoot":"","sources":["../src/Boolean.ts"],"names":[],"mappings":";;AAKA,8BAKC;AARD,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1C,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAE1C,SAAgB,SAAS,CAAC,KAAc;IACtC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IACpC,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare const CapturedAtTagNames: readonly ["SubSecDateTimeOriginal", "SubSecCreateDate", "SubSecMediaCreateDate", "DateTimeOriginal", "CreateDate", "MediaCreateDate", "CreationDate", "DateTimeCreated", "TimeCreated"];
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CapturedAtTagNames = void 0;
|
||||
exports.CapturedAtTagNames = [
|
||||
"SubSecDateTimeOriginal",
|
||||
"SubSecCreateDate",
|
||||
"SubSecMediaCreateDate",
|
||||
"DateTimeOriginal",
|
||||
"CreateDate",
|
||||
"MediaCreateDate",
|
||||
"CreationDate", // < Found in some transcoded Apple movies
|
||||
"DateTimeCreated",
|
||||
"TimeCreated", // < may not have the date
|
||||
];
|
||||
//# sourceMappingURL=CapturedAtTagNames.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CapturedAtTagNames.js","sourceRoot":"","sources":["../src/CapturedAtTagNames.ts"],"names":[],"mappings":";;;AAEa,QAAA,kBAAkB,GAAG;IAChC,wBAAwB;IACxB,kBAAkB;IAClB,uBAAuB;IACvB,kBAAkB;IAClB,YAAY;IACZ,iBAAiB;IACjB,cAAc,EAAE,0CAA0C;IAC1D,iBAAiB;IACjB,aAAa,EAAE,0BAA0B;CACC,CAAC"}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Found in newer Android Motion Photos
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/pull/189
|
||||
*/
|
||||
export interface ContainerDirectoryItem {
|
||||
Item: {
|
||||
Length?: number;
|
||||
Mime?: string;
|
||||
Padding?: number;
|
||||
Semantic?: string;
|
||||
};
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=ContainerDirectoryItem.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ContainerDirectoryItem.js","sourceRoot":"","sources":["../src/ContainerDirectoryItem.ts"],"names":[],"mappings":""}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { Maybe } from "./Maybe";
|
||||
type Direction = "N" | "S" | "E" | "W";
|
||||
type CoordinateFormat = "DMS" | "DM" | "D";
|
||||
interface Coordinate {
|
||||
decimal: number;
|
||||
degrees: number;
|
||||
minutes: number | undefined;
|
||||
seconds: number | undefined;
|
||||
direction: Direction | undefined;
|
||||
format: CoordinateFormat;
|
||||
remainder: string | undefined;
|
||||
}
|
||||
export interface CoordinateResult {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
||||
/**
|
||||
* Parses a string containing both latitude and longitude coordinates.
|
||||
* @param input - String containing both coordinates
|
||||
* @returns Object containing latitude and longitude in decimal degrees, or undefined if parsing fails
|
||||
* @throws CoordinateParseError if the input format is invalid
|
||||
*/
|
||||
export declare function parseCoordinates(input: string): CoordinateResult;
|
||||
/**
|
||||
* Parses a coordinate string in decimal degrees format.
|
||||
* @param input - String containing a single coordinate
|
||||
* @returns Object containing degrees and direction, or undefined if parsing fails
|
||||
* @throws CoordinateParseError if the format is not decimal degrees or direction is missing
|
||||
*/
|
||||
export declare function parseDecimalCoordinate(input: string): {
|
||||
decimal: number;
|
||||
direction: Direction;
|
||||
} | undefined;
|
||||
/**
|
||||
* Parses a single coordinate string into its components.
|
||||
* @param input - String containing a single coordinate
|
||||
* @param expectRemainders - If true, allow additional text after the coordinate
|
||||
* @returns Parsed coordinate object
|
||||
* @throws CoordinateParseError if the format is invalid
|
||||
*/
|
||||
export declare function parseCoordinate(input: string, expectRemainders?: boolean): Coordinate;
|
||||
export type CoordinateType = "Latitude" | "Longitude";
|
||||
export interface CoordinateConfig {
|
||||
value: number;
|
||||
ref: string | undefined;
|
||||
geoValue: number | undefined;
|
||||
expectedRefPositive: "N" | "E";
|
||||
expectedRefNegative: "S" | "W";
|
||||
max: 90 | 180;
|
||||
coordinateType: CoordinateType;
|
||||
}
|
||||
export declare function roundGpsDecimal(decimal: number): number;
|
||||
export declare function parsePosition(position: Maybe<string>): Maybe<[number, number]>;
|
||||
export declare function processCoordinate(config: CoordinateConfig, warnings: string[]): {
|
||||
value: number;
|
||||
ref: string;
|
||||
isInvalid: boolean;
|
||||
};
|
||||
export {};
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseCoordinates = parseCoordinates;
|
||||
exports.parseDecimalCoordinate = parseDecimalCoordinate;
|
||||
exports.parseCoordinate = parseCoordinate;
|
||||
exports.roundGpsDecimal = roundGpsDecimal;
|
||||
exports.parsePosition = parsePosition;
|
||||
exports.processCoordinate = processCoordinate;
|
||||
const Number_1 = require("./Number");
|
||||
const String_1 = require("./String");
|
||||
// Constants
|
||||
const MAX_LATITUDE_DEGREES = 90;
|
||||
const MAX_LONGITUDE_DEGREES = 180;
|
||||
class CoordinateParseError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = "CoordinateParseError";
|
||||
}
|
||||
}
|
||||
// Regex to match simple decimal coordinates, like "37.5, -122.5"
|
||||
const DecimalCoordsRE = /^(-?\d+(?:\.\d+)?)[,\s]+(-?\d+(?:\.\d+)?)$/;
|
||||
/**
|
||||
* Parses a string containing both latitude and longitude coordinates.
|
||||
* @param input - String containing both coordinates
|
||||
* @returns Object containing latitude and longitude in decimal degrees, or undefined if parsing fails
|
||||
* @throws CoordinateParseError if the input format is invalid
|
||||
*/
|
||||
function parseCoordinates(input) {
|
||||
input = (0, String_1.toS)(input).trim();
|
||||
if (input.length === 0) {
|
||||
throw new CoordinateParseError("Input string cannot be empty");
|
||||
}
|
||||
if (DecimalCoordsRE.test(input)) {
|
||||
const split = input.split(/[\s,]+/);
|
||||
const [latitude, longitude] = split
|
||||
.map(Number_1.toFloat)
|
||||
.map((ea) => (ea == null ? null : roundGpsDecimal(ea)));
|
||||
if (latitude == null || longitude == null) {
|
||||
throw new CoordinateParseError("Failed to parse decimal coordinates");
|
||||
}
|
||||
return { latitude, longitude };
|
||||
}
|
||||
let latitude;
|
||||
let longitude;
|
||||
for (const coord of parseStringCoordinates(input)) {
|
||||
if (!coord.direction) {
|
||||
throw new CoordinateParseError("Direction is required for position parsing");
|
||||
}
|
||||
if (coord.direction === "S" || coord.direction === "N") {
|
||||
if (latitude !== undefined) {
|
||||
throw new CoordinateParseError("Multiple latitude values found");
|
||||
}
|
||||
latitude = toDecimalDegrees(coord);
|
||||
}
|
||||
else {
|
||||
if (longitude != null) {
|
||||
throw new CoordinateParseError("Multiple longitude values found");
|
||||
}
|
||||
longitude = toDecimalDegrees(coord);
|
||||
}
|
||||
}
|
||||
const missing = [];
|
||||
if (latitude == null)
|
||||
missing.push("latitude");
|
||||
if (longitude == null)
|
||||
missing.push("longitude");
|
||||
if (latitude == null || longitude == null) {
|
||||
throw new CoordinateParseError(`Missing ${missing.join(" and ")}`);
|
||||
}
|
||||
else {
|
||||
return { latitude, longitude };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses a string containing one or more coordinates.
|
||||
* @param input - String containing coordinates
|
||||
* @returns Array of parsed coordinates
|
||||
*/
|
||||
function parseStringCoordinates(input) {
|
||||
if (!input?.trim()) {
|
||||
throw new CoordinateParseError("Input string cannot be empty");
|
||||
}
|
||||
const lat = parseCoordinate(input, true);
|
||||
const remainders = lat.remainder;
|
||||
if ((0, String_1.blank)(remainders)) {
|
||||
throw new CoordinateParseError("Expected multiple coordinates");
|
||||
}
|
||||
return [lat, parseCoordinate(remainders)];
|
||||
}
|
||||
/**
|
||||
* Parses a coordinate string in decimal degrees format.
|
||||
* @param input - String containing a single coordinate
|
||||
* @returns Object containing degrees and direction, or undefined if parsing fails
|
||||
* @throws CoordinateParseError if the format is not decimal degrees or direction is missing
|
||||
*/
|
||||
function parseDecimalCoordinate(input) {
|
||||
if (!input?.trim()) {
|
||||
throw new CoordinateParseError("Input string cannot be empty");
|
||||
}
|
||||
const coord = parseCoordinate(input);
|
||||
if (coord.format !== "D") {
|
||||
throw new CoordinateParseError("Expected decimal degrees format");
|
||||
}
|
||||
if (!coord.direction) {
|
||||
throw new CoordinateParseError("Missing direction");
|
||||
}
|
||||
return { decimal: toDecimalDegrees(coord), direction: coord.direction };
|
||||
}
|
||||
const DecimalCoordRE = /^(-?\d+(?:\.\d+)?)$/;
|
||||
/**
|
||||
* Parses a single coordinate string into its components.
|
||||
* @param input - String containing a single coordinate
|
||||
* @param expectRemainders - If true, allow additional text after the coordinate
|
||||
* @returns Parsed coordinate object
|
||||
* @throws CoordinateParseError if the format is invalid
|
||||
*/
|
||||
function parseCoordinate(input, expectRemainders = false) {
|
||||
input = (0, String_1.toS)(input).trim();
|
||||
if (input.length === 0) {
|
||||
throw new CoordinateParseError("Input string cannot be empty");
|
||||
}
|
||||
if (DecimalCoordRE.test(input)) {
|
||||
const f = (0, Number_1.toFloat)(input);
|
||||
if (f == null) {
|
||||
throw new CoordinateParseError("Failed to parse decimal coordinate");
|
||||
}
|
||||
const r = roundGpsDecimal(f);
|
||||
return {
|
||||
degrees: r,
|
||||
decimal: r,
|
||||
format: "D",
|
||||
direction: undefined,
|
||||
minutes: undefined,
|
||||
seconds: undefined,
|
||||
remainder: "",
|
||||
};
|
||||
}
|
||||
const dmsPattern = /^(?<degrees>-?\d+)\s*(?:°|DEG)\s*(?<minutes>\d+)\s*['′]\s*(?<seconds>\d+(?:\.\d+)?)\s*["″]\s?(?<direction>[NSEW])?[\s,]{0,3}(?<remainder>.*)$/i;
|
||||
const dmPattern = /^(?<degrees>-?\d+)\s*(?:°|DEG)\s*(?<minutes>\d+(?:\.\d+)?)\s?['′]\s?(?<direction>[NSEW])?(?<remainder>.*)$/i;
|
||||
const dPattern = /^(?<degrees>-?\d+(?:\.\d+)?)\s*(?:°|DEG)\s?(?<direction>[NSEW])?(?<remainder>.*)$/i;
|
||||
const trimmedInput = input.trimStart();
|
||||
let match;
|
||||
let format = null;
|
||||
if ((match = trimmedInput.match(dmsPattern))) {
|
||||
format = "DMS";
|
||||
}
|
||||
else if ((match = trimmedInput.match(dmPattern))) {
|
||||
format = "DM";
|
||||
}
|
||||
else if ((match = trimmedInput.match(dPattern))) {
|
||||
format = "D";
|
||||
}
|
||||
if (match == null ||
|
||||
format == null ||
|
||||
(!expectRemainders && !(0, String_1.blank)(match?.groups?.remainder))) {
|
||||
throw new CoordinateParseError("Invalid coordinate format. Expected one of:\n" +
|
||||
" DDD° MM' SS.S\" k (deg/min/sec)\n" +
|
||||
" DDD° MM.MMM' k (deg/decimal minutes)\n" +
|
||||
" DDD.DDDDD° (decimal degrees)\n" +
|
||||
" (where k indicates direction: N, S, E, or W)");
|
||||
}
|
||||
if (!match.groups) {
|
||||
throw new CoordinateParseError("Failed to parse coordinate components");
|
||||
}
|
||||
const { degrees: degreesStr, minutes: minutesStr, seconds: secondsStr, direction: directionStr, remainder, } = match.groups;
|
||||
const direction = directionStr?.toUpperCase();
|
||||
const degrees = parseFloat(degreesStr);
|
||||
let minutes;
|
||||
let seconds;
|
||||
if (format === "DMS") {
|
||||
minutes = parseInt(minutesStr, 10);
|
||||
seconds = parseFloat(secondsStr);
|
||||
if (minutes >= 60) {
|
||||
throw new CoordinateParseError("Minutes must be between 0 and 59");
|
||||
}
|
||||
if (seconds >= 60) {
|
||||
throw new CoordinateParseError("Seconds must be between 0 and 59.999...");
|
||||
}
|
||||
}
|
||||
else if (format === "DM") {
|
||||
minutes = parseFloat(minutesStr);
|
||||
if (minutes >= 60) {
|
||||
throw new CoordinateParseError("Minutes must be between 0 and 59.999...");
|
||||
}
|
||||
}
|
||||
const maxDegrees = direction === "N" || direction === "S"
|
||||
? MAX_LATITUDE_DEGREES
|
||||
: MAX_LONGITUDE_DEGREES;
|
||||
if (Math.abs(degrees) > maxDegrees) {
|
||||
throw new CoordinateParseError(`Degrees must be between -${maxDegrees} and ${maxDegrees} for ${direction} direction`);
|
||||
}
|
||||
const coords = {
|
||||
degrees,
|
||||
minutes,
|
||||
seconds,
|
||||
direction,
|
||||
format,
|
||||
remainder: remainder?.trim(),
|
||||
};
|
||||
const decimal = toDecimalDegrees(coords);
|
||||
return {
|
||||
...coords,
|
||||
decimal,
|
||||
};
|
||||
}
|
||||
function toDecimalDegrees(coord) {
|
||||
const degrees = (0, Number_1.toFloat)(coord.degrees) ?? 0;
|
||||
const sign = Math.sign(degrees);
|
||||
let decimal = Math.abs(degrees);
|
||||
decimal += Math.abs((0, Number_1.toFloat)(coord.minutes) ?? 0) / 60.0;
|
||||
decimal += Math.abs((0, Number_1.toFloat)(coord.seconds) ?? 0) / 3600.0;
|
||||
if (coord.direction === "S" || coord.direction === "W" || sign < 0) {
|
||||
decimal = -decimal;
|
||||
}
|
||||
const maxDegrees = coord.direction === "N" || coord.direction === "S"
|
||||
? MAX_LATITUDE_DEGREES
|
||||
: MAX_LONGITUDE_DEGREES;
|
||||
const axis = coord.direction === "N" || coord.direction === "S"
|
||||
? "latitude"
|
||||
: "longitude";
|
||||
if (Math.abs(decimal) > maxDegrees) {
|
||||
throw new CoordinateParseError(`Degrees must be between -${maxDegrees} and ${maxDegrees} for ${axis}`);
|
||||
}
|
||||
// Round to 6 decimal places
|
||||
// Most consumer devices can only resolve 4-5 decimal places (1m resolution)
|
||||
return roundGpsDecimal(decimal);
|
||||
}
|
||||
const MAX_LAT_LON_DIFF = 1;
|
||||
function roundGpsDecimal(decimal) {
|
||||
return (0, Number_1.roundToDecimalPlaces)(decimal, 6);
|
||||
}
|
||||
function parsePosition(position) {
|
||||
if ((0, String_1.blank)(position))
|
||||
return;
|
||||
const [lat, lon] = (0, String_1.toS)(position).split(/[, ]+/).map(Number_1.toFloat);
|
||||
return lat != null && lon != null ? [lat, lon] : undefined;
|
||||
}
|
||||
function processCoordinate(config, warnings) {
|
||||
let { value, ref } = config;
|
||||
const { geoValue, coordinateType } = config;
|
||||
const { expectedRefPositive, expectedRefNegative, max } = config;
|
||||
let isInvalid = false;
|
||||
// Validate ref is reasonable -- it should either start with
|
||||
// expectedRefPositive or expectedRefNegative:
|
||||
ref = (0, String_1.toS)(ref).trim().toUpperCase().slice(0, 1);
|
||||
if (!(0, String_1.blank)(config.ref) &&
|
||||
ref !== expectedRefPositive &&
|
||||
ref !== expectedRefNegative) {
|
||||
warnings.push(`Invalid GPS${coordinateType}Ref: ${JSON.stringify(config.ref)}.`);
|
||||
ref = value < 0 ? expectedRefNegative : expectedRefPositive;
|
||||
}
|
||||
// Check range
|
||||
if (Math.abs(value) > max) {
|
||||
isInvalid = true;
|
||||
warnings.push(`Invalid GPS${coordinateType}: ${value} is out of range`);
|
||||
return { value, ref, isInvalid };
|
||||
}
|
||||
// Apply hemisphere reference
|
||||
if (ref === expectedRefNegative) {
|
||||
value = -Math.abs(value);
|
||||
}
|
||||
// Check for mismatched signs with GeolocationPosition
|
||||
if (geoValue != null &&
|
||||
Math.abs(Math.abs(geoValue) - Math.abs(value)) < MAX_LAT_LON_DIFF) {
|
||||
if (Math.sign(geoValue) !== Math.sign(value)) {
|
||||
value = -value;
|
||||
warnings.push(`Corrected GPS${coordinateType} sign based on GeolocationPosition`);
|
||||
}
|
||||
// Force ref to correct value
|
||||
const expectedRef = geoValue < 0 ? expectedRefNegative : expectedRefPositive;
|
||||
if (ref !== expectedRef) {
|
||||
ref = expectedRef;
|
||||
if (!(0, String_1.blank)(config.ref)) {
|
||||
warnings.push(`Corrected GPS${coordinateType}Ref to ${expectedRef} based on GeolocationPosition`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure ref matches coordinate sign
|
||||
const expectedRef = value < 0 ? expectedRefNegative : expectedRefPositive;
|
||||
if (ref != null && ref !== expectedRef && !(0, String_1.blank)(config.ref)) {
|
||||
warnings.push(`Corrected GPS${coordinateType}Ref to ${ref} to match coordinate sign`);
|
||||
}
|
||||
ref = expectedRef;
|
||||
return { value: roundGpsDecimal(value), ref, isInvalid };
|
||||
}
|
||||
//# sourceMappingURL=CoordinateParser.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CoordinateParser.js","sourceRoot":"","sources":["../src/CoordinateParser.ts"],"names":[],"mappings":"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 truncated
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { DateTime } from "luxon";
|
||||
import { ExifDate } from "./ExifDate";
|
||||
import { ExifDateTime } from "./ExifDateTime";
|
||||
import { ExifTime } from "./ExifTime";
|
||||
import { Maybe } from "./Maybe";
|
||||
export declare function validDateTime(dt: Maybe<DateTime>): dt is DateTime;
|
||||
export declare const SecondMs = 1000;
|
||||
export declare const MinuteMs: number;
|
||||
export declare const HourMs: number;
|
||||
export declare const DayMs: number;
|
||||
export type DateOrTime = ExifDateTime | ExifDate | ExifTime | DateTime;
|
||||
export declare function isDateOrTime(o: unknown): o is DateOrTime;
|
||||
export declare function dateTimeToExif(d: DateTime, opts?: {
|
||||
includeOffset?: boolean;
|
||||
includeMilliseconds?: boolean;
|
||||
}): string;
|
||||
export declare function toExifString(d: DateOrTime): Maybe<string>;
|
||||
export declare function hms(d: DateTime, opts?: {
|
||||
includeMilliseconds?: boolean;
|
||||
}): string;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DayMs = exports.HourMs = exports.MinuteMs = exports.SecondMs = void 0;
|
||||
exports.validDateTime = validDateTime;
|
||||
exports.isDateOrTime = isDateOrTime;
|
||||
exports.dateTimeToExif = dateTimeToExif;
|
||||
exports.toExifString = toExifString;
|
||||
exports.hms = hms;
|
||||
const luxon_1 = require("luxon");
|
||||
const ExifDate_1 = require("./ExifDate");
|
||||
const ExifDateTime_1 = require("./ExifDateTime");
|
||||
const ExifTime_1 = require("./ExifTime");
|
||||
function validDateTime(dt) {
|
||||
return dt != null && dt.isValid;
|
||||
}
|
||||
exports.SecondMs = 1000;
|
||||
exports.MinuteMs = 60 * exports.SecondMs;
|
||||
exports.HourMs = 60 * exports.MinuteMs;
|
||||
exports.DayMs = 24 * exports.HourMs;
|
||||
function isDateOrTime(o) {
|
||||
return (o instanceof ExifDateTime_1.ExifDateTime ||
|
||||
o instanceof ExifDate_1.ExifDate ||
|
||||
o instanceof ExifTime_1.ExifTime ||
|
||||
luxon_1.DateTime.isDateTime(o));
|
||||
}
|
||||
function dateTimeToExif(d, opts) {
|
||||
return d.toFormat("y:MM:dd HH:mm:ss" +
|
||||
(opts?.includeMilliseconds === true ? ".u" : "") +
|
||||
(opts?.includeOffset === false ? "" : "ZZ"));
|
||||
}
|
||||
function toExifString(d) {
|
||||
if (luxon_1.DateTime.isDateTime(d)) {
|
||||
return dateTimeToExif(d);
|
||||
}
|
||||
else {
|
||||
return d?.toExifString?.();
|
||||
}
|
||||
}
|
||||
function hms(d, opts) {
|
||||
return d.toFormat("HH:mm:ss" + (opts?.includeMilliseconds === true ? ".SSS" : ""));
|
||||
}
|
||||
//# sourceMappingURL=DateTime.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DateTime.js","sourceRoot":"","sources":["../src/DateTime.ts"],"names":[],"mappings":";;;AAMA,sCAEC;AASD,oCAOC;AAED,wCASC;AAED,oCAMC;AAED,kBAOC;AApDD,iCAAiC;AACjC,yCAAsC;AACtC,iDAA8C;AAC9C,yCAAsC;AAGtC,SAAgB,aAAa,CAAC,EAAmB;IAC/C,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,OAAO,CAAC;AAClC,CAAC;AAEY,QAAA,QAAQ,GAAG,IAAI,CAAC;AAChB,QAAA,QAAQ,GAAG,EAAE,GAAG,gBAAQ,CAAC;AACzB,QAAA,MAAM,GAAG,EAAE,GAAG,gBAAQ,CAAC;AACvB,QAAA,KAAK,GAAG,EAAE,GAAG,cAAM,CAAC;AAIjC,SAAgB,YAAY,CAAC,CAAU;IACrC,OAAO,CACL,CAAC,YAAY,2BAAY;QACzB,CAAC,YAAY,mBAAQ;QACrB,CAAC,YAAY,mBAAQ;QACrB,gBAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CACvB,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAC5B,CAAW,EACX,IAAiE;IAEjE,OAAO,CAAC,CAAC,QAAQ,CACf,kBAAkB;QAChB,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,CAAC,IAAI,EAAE,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAC9C,CAAC;AACJ,CAAC;AAED,SAAgB,YAAY,CAAC,CAAa;IACxC,IAAI,gBAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,SAAgB,GAAG,CACjB,CAAW,EACX,IAAwC;IAExC,OAAO,CAAC,CAAC,QAAQ,CACf,UAAU,GAAG,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAChE,CAAC;AACJ,CAAC"}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* @deprecated use {@link CapturedAtTagNames} instead.
|
||||
*/
|
||||
export declare const DefaultCreateDateTagNames: readonly ["SubSecDateTimeOriginal", "SubSecCreateDate", "SubSecMediaCreateDate", "DateTimeOriginal", "CreateDate", "MediaCreateDate", "CreationDate", "DateTimeCreated", "TimeCreated"];
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultCreateDateTagNames = void 0;
|
||||
const CapturedAtTagNames_1 = require("./CapturedAtTagNames");
|
||||
/**
|
||||
* @deprecated use {@link CapturedAtTagNames} instead.
|
||||
*/
|
||||
exports.DefaultCreateDateTagNames = CapturedAtTagNames_1.CapturedAtTagNames;
|
||||
//# sourceMappingURL=DefaultCreateDateTagNames.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultCreateDateTagNames.js","sourceRoot":"","sources":["../src/DefaultCreateDateTagNames.ts"],"names":[],"mappings":";;;AAAA,6DAA0D;AAE1D;;GAEG;AACU,QAAA,yBAAyB,GAAG,uCAAkB,CAAC"}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import * as bc from "batch-cluster";
|
||||
import { ExifToolOptions } from "./ExifToolOptions";
|
||||
import { Omit } from "./Omit";
|
||||
import { Tags } from "./Tags";
|
||||
export declare const ConsoleLogger: bc.Logger;
|
||||
/**
|
||||
* Default values for `ExifToolOptions`, except for `processFactory` (which is
|
||||
* created by the ExifTool constructor)
|
||||
*/
|
||||
export declare const DefaultExifToolOptions: Omit<ExifToolOptions, "processFactory" | "ignoreShebang">;
|
||||
/**
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/215
|
||||
*/
|
||||
export declare function defaultAdjustTimeZoneIfDaylightSavings(t: Tags): number | undefined;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultExifToolOptions = exports.ConsoleLogger = void 0;
|
||||
exports.defaultAdjustTimeZoneIfDaylightSavings = defaultAdjustTimeZoneIfDaylightSavings;
|
||||
const bc = __importStar(require("batch-cluster"));
|
||||
const node_util_1 = require("node:util");
|
||||
const Boolean_1 = require("./Boolean");
|
||||
const CapturedAtTagNames_1 = require("./CapturedAtTagNames");
|
||||
const DefaultExiftoolArgs_1 = require("./DefaultExiftoolArgs");
|
||||
const DefaultMaxProcs_1 = require("./DefaultMaxProcs");
|
||||
const ExiftoolPath_1 = require("./ExiftoolPath");
|
||||
const GeoTz_1 = require("./GeoTz");
|
||||
const IsWin32_1 = require("./IsWin32");
|
||||
const VersionTask_1 = require("./VersionTask");
|
||||
const _debuglog = (0, node_util_1.debuglog)("exiftool-vendored");
|
||||
function noop() { }
|
||||
exports.ConsoleLogger = {
|
||||
trace: noop,
|
||||
debug: _debuglog,
|
||||
info: _debuglog,
|
||||
warn: console.warn,
|
||||
error: console.error,
|
||||
};
|
||||
function logger() {
|
||||
return (0, node_util_1.debuglog)("exiftool-vendored").enabled ? exports.ConsoleLogger : bc.NoLogger;
|
||||
}
|
||||
/**
|
||||
* Default values for `ExifToolOptions`, except for `processFactory` (which is
|
||||
* created by the ExifTool constructor)
|
||||
*/
|
||||
exports.DefaultExifToolOptions = Object.freeze({
|
||||
...new bc.BatchClusterOptions(),
|
||||
maxProcs: DefaultMaxProcs_1.DefaultMaxProcs,
|
||||
maxTasksPerProcess: 500,
|
||||
spawnTimeoutMillis: 30000,
|
||||
streamFlushMillis: 10,
|
||||
// see https://github.com/photostructure/exiftool-vendored.js/issues/34 :
|
||||
taskTimeoutMillis: 20000,
|
||||
onIdleIntervalMillis: 2000,
|
||||
taskRetries: 1,
|
||||
exiftoolPath: ExiftoolPath_1.exiftoolPath,
|
||||
exiftoolArgs: DefaultExiftoolArgs_1.DefaultExiftoolArgs,
|
||||
exiftoolEnv: {},
|
||||
checkPerl: !(0, IsWin32_1.isWin32)(),
|
||||
pass: "{ready}",
|
||||
fail: "{ready}",
|
||||
exitCommand: "-stay_open\nFalse\n",
|
||||
versionCommand: new VersionTask_1.VersionTask().command,
|
||||
healthCheckIntervalMillis: 30000,
|
||||
healthCheckCommand: "-ver\n-execute\n",
|
||||
backfillTimezones: true,
|
||||
defaultVideosToUTC: true,
|
||||
geoTz: GeoTz_1.geoTz,
|
||||
geolocation: false,
|
||||
ignoreZeroZeroLatLon: true,
|
||||
ignoreMinorErrors: true,
|
||||
forceWrite: false,
|
||||
imageHashType: false,
|
||||
includeImageDataMD5: undefined,
|
||||
inferTimezoneFromDatestamps: false, // to retain prior behavior
|
||||
inferTimezoneFromDatestampTags: [...CapturedAtTagNames_1.CapturedAtTagNames],
|
||||
inferTimezoneFromTimeStamp: false, // to retain prior behavior
|
||||
logger,
|
||||
numericTags: [
|
||||
"*Duration*",
|
||||
"GPSAltitude",
|
||||
"GPSLatitude",
|
||||
"GPSLongitude",
|
||||
"GPSPosition",
|
||||
"GeolocationPosition",
|
||||
"Orientation",
|
||||
// NOT Rotation! Rotation can be encoded as degrees rotated clockwise, or a
|
||||
// EXIF-Orientation string (!!). If we ask ExifTool for numeric rotations of HEICs,
|
||||
// we get "3" (when it means "Rotate 90 CW"):
|
||||
// $ exiftool -j -Rotation -Orientation IMG_6947.HEIC
|
||||
// [{
|
||||
// "Rotation": "Rotate 90 CW",
|
||||
// "Orientation": "Rotate 90 CW"
|
||||
// }]
|
||||
// $ exiftool -j -Rotation# -Orientation# IMG_6947.HEIC
|
||||
// [{
|
||||
// "Rotation": 3, // < WTH is this? 3 means 180º (!?)
|
||||
// "Orientation": 6 // < expected
|
||||
// }]
|
||||
],
|
||||
useMWG: false,
|
||||
struct: 1,
|
||||
readArgs: ["-fast"],
|
||||
writeArgs: [],
|
||||
adjustTimeZoneIfDaylightSavings: defaultAdjustTimeZoneIfDaylightSavings,
|
||||
preferTimezoneInferenceFromGps: false, // to retain prior behavior
|
||||
keepUTCTime: true,
|
||||
});
|
||||
/**
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/215
|
||||
*/
|
||||
function defaultAdjustTimeZoneIfDaylightSavings(t) {
|
||||
// `DaylightSavings` may be "Yes" or `true`:
|
||||
return true === (0, Boolean_1.toBoolean)(t.DaylightSavings) &&
|
||||
// Daggum Nikon likes "FS-Nikon", "Nikon", "NIKON", and "NIKON CORPORATION"
|
||||
/\bnikon\b/i.test(String(t.Make))
|
||||
? 60
|
||||
: undefined;
|
||||
}
|
||||
//# sourceMappingURL=DefaultExifToolOptions.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultExifToolOptions.js","sourceRoot":"","sources":["../src/DefaultExifToolOptions.ts"],"names":[],"mappings":"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"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare const DefaultExiftoolArgs: string[];
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultExiftoolArgs = void 0;
|
||||
// -stay_open True -@ -
|
||||
exports.DefaultExiftoolArgs = ["-stay_open", "True", "-@", "-"];
|
||||
//# sourceMappingURL=DefaultExiftoolArgs.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultExiftoolArgs.js","sourceRoot":"","sources":["../src/DefaultExiftoolArgs.ts"],"names":[],"mappings":";;;AAAA,uBAAuB;AACV,QAAA,mBAAmB,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare const DefaultMaxProcs: number;
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultMaxProcs = void 0;
|
||||
const _os = __importStar(require("node:os"));
|
||||
const Object_1 = require("./Object");
|
||||
function maxCpus() {
|
||||
// https://nodejs.org/api/os.html#osavailableparallelism was added in node
|
||||
// 18.14, but people may still be running archaic versions of node.
|
||||
return Math.max(1, (0, Object_1.isFunction)(_os.availableParallelism)
|
||||
? _os.availableParallelism()
|
||||
: _os.cpus().length);
|
||||
}
|
||||
exports.DefaultMaxProcs = Math.ceil(maxCpus() / 4);
|
||||
//# sourceMappingURL=DefaultMaxProcs.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultMaxProcs.js","sourceRoot":"","sources":["../src/DefaultMaxProcs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAA+B;AAC/B,qCAAsC;AAEtC,SAAS,OAAO;IACd,0EAA0E;IAC1E,mEAAmE;IAEnE,OAAO,IAAI,CAAC,GAAG,CACb,CAAC,EACD,IAAA,mBAAU,EAAC,GAAG,CAAC,oBAAoB,CAAC;QAClC,CAAC,CAAC,GAAG,CAAC,oBAAoB,EAAE;QAC5B,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CACtB,CAAC;AACJ,CAAC;AAEY,QAAA,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC"}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export type Defined<T> = T extends undefined ? never : T;
|
||||
export type DefinedOrNullValued<T> = {
|
||||
[P in keyof T]: Defined<T[P]> | null;
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=Defined.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Defined.js","sourceRoot":"","sources":["../src/Defined.ts"],"names":[],"mappings":""}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare const DeleteAllTagsArgs: string[];
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DeleteAllTagsArgs = void 0;
|
||||
exports.DeleteAllTagsArgs = ["-all="];
|
||||
//# sourceMappingURL=DeleteAllTagsArgs.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DeleteAllTagsArgs.js","sourceRoot":"","sources":["../src/DeleteAllTagsArgs.ts"],"names":[],"mappings":";;;AAAa,QAAA,iBAAiB,GAAG,CAAC,OAAO,CAAC,CAAC"}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ExifToolTask } from "./ExifToolTask";
|
||||
export interface ErrorsAndWarnings {
|
||||
/**
|
||||
* This is a list of all critical errors raised by ExifTool during the read
|
||||
* process.
|
||||
*/
|
||||
errors?: string[];
|
||||
/**
|
||||
* This is a list of all non-critical errors raised by ExifTool during the
|
||||
* read process.
|
||||
*
|
||||
* Invalid field values are considered warnings and not errors, for example.
|
||||
*/
|
||||
warnings?: string[];
|
||||
}
|
||||
export declare function errorsAndWarnings(task: ExifToolTask<unknown>, t?: {
|
||||
Error?: string;
|
||||
Warning?: string;
|
||||
}): Required<ErrorsAndWarnings>;
|
||||
/**
|
||||
* Convert an unknown value to an Error.
|
||||
*/
|
||||
export declare function toError(e: unknown, messageIfBlank?: string): Error;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.errorsAndWarnings = errorsAndWarnings;
|
||||
exports.toError = toError;
|
||||
const Array_1 = require("./Array");
|
||||
const String_1 = require("./String");
|
||||
function errorsAndWarnings(task, t) {
|
||||
return {
|
||||
errors: (0, Array_1.uniq)((0, String_1.compactBlanks)([t?.Error, ...task.errors])),
|
||||
warnings: (0, Array_1.uniq)((0, String_1.compactBlanks)([t?.Warning, ...task.warnings])),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert an unknown value to an Error.
|
||||
*/
|
||||
function toError(e, messageIfBlank = "Unknown error") {
|
||||
return e instanceof Error
|
||||
? e
|
||||
: new Error((0, String_1.toNotBlank)((0, String_1.toS)(e)) ?? messageIfBlank);
|
||||
}
|
||||
//# sourceMappingURL=ErrorsAndWarnings.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ErrorsAndWarnings.js","sourceRoot":"","sources":["../src/ErrorsAndWarnings.ts"],"names":[],"mappings":";;AAoBA,8CAQC;AAKD,0BAIC;AArCD,mCAA+B;AAE/B,qCAA0D;AAkB1D,SAAgB,iBAAiB,CAC/B,IAA2B,EAC3B,CAAwC;IAExC,OAAO;QACL,MAAM,EAAE,IAAA,YAAI,EAAC,IAAA,sBAAa,EAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACvD,QAAQ,EAAE,IAAA,YAAI,EAAC,IAAA,sBAAa,EAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;KAC9D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,OAAO,CAAC,CAAU,EAAE,cAAc,GAAG,eAAe;IAClE,OAAO,CAAC,YAAY,KAAK;QACvB,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,KAAK,CAAC,IAAA,mBAAU,EAAC,IAAA,YAAG,EAAC,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC;AACtD,CAAC"}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { DateTime } from "luxon";
|
||||
import { Maybe } from "./Maybe";
|
||||
/**
|
||||
* Encodes an ExifDate
|
||||
*/
|
||||
export declare class ExifDate {
|
||||
readonly year: number;
|
||||
readonly month: number;
|
||||
readonly day: number;
|
||||
readonly rawValue?: string | undefined;
|
||||
static from(exifOrIso: string): Maybe<ExifDate>;
|
||||
static fromISO(text: string): Maybe<ExifDate>;
|
||||
private static fromPatterns;
|
||||
static fromExifStrict(text: string): Maybe<ExifDate>;
|
||||
static fromExifLoose(text: string): Maybe<ExifDate>;
|
||||
static fromEXIF(text: string): Maybe<ExifDate>;
|
||||
static fromDateTime(dt: DateTime, rawValue?: string): Maybe<ExifDate>;
|
||||
constructor(year: number, // full year (probably 2019-ish, but maybe Japanese 30-ish). See https://ericasadun.com/2018/12/25/iso-8601-yyyy-yyyy-and-why-your-year-may-be-wrong/
|
||||
month: number, // 1-12, (no crazy 0-11 nonsense from Date!)
|
||||
day: number, // 1-31
|
||||
rawValue?: string | undefined);
|
||||
toDate(): Date;
|
||||
/**
|
||||
* @param deltaMs defaults to 12 hours, so toMillis() is in the middle of the day.
|
||||
*
|
||||
* @return the epoch milliseconds for this day in UTC, plus `deltaMs` milliseconds.
|
||||
*/
|
||||
toMillis(deltaMs?: number): number;
|
||||
toISOString(): string;
|
||||
toExifString(): string;
|
||||
toString(sep?: string): string;
|
||||
toJSON(): {
|
||||
_ctor: string;
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
rawValue: string | undefined;
|
||||
};
|
||||
static fromJSON(json: ReturnType<ExifDate["toJSON"]>): ExifDate;
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifDate = void 0;
|
||||
const luxon_1 = require("luxon");
|
||||
const DateTime_1 = require("./DateTime");
|
||||
const Maybe_1 = require("./Maybe");
|
||||
const String_1 = require("./String");
|
||||
const StrictExifRE = /^\d{1,4}:\d{1,2}:\d{1,2}|\d{1,4}-\d{1,2}-\d{1,2}$/;
|
||||
const LooseExifRE = /^\S+\s+\S+\s+\S+$/;
|
||||
/**
|
||||
* Encodes an ExifDate
|
||||
*/
|
||||
class ExifDate {
|
||||
year;
|
||||
month;
|
||||
day;
|
||||
rawValue;
|
||||
static from(exifOrIso) {
|
||||
return (
|
||||
// in order of strictness:
|
||||
this.fromExifStrict(exifOrIso) ??
|
||||
this.fromISO(exifOrIso) ??
|
||||
this.fromExifLoose(exifOrIso));
|
||||
}
|
||||
static fromISO(text) {
|
||||
return StrictExifRE.test((0, String_1.toS)(text).trim())
|
||||
? this.fromDateTime(luxon_1.DateTime.fromISO(text), text)
|
||||
: undefined;
|
||||
}
|
||||
static fromPatterns(text, fmts) {
|
||||
if ((0, String_1.blank)(text))
|
||||
return;
|
||||
text = (0, String_1.toS)(text).trim();
|
||||
for (const fmt of fmts) {
|
||||
const dt = luxon_1.DateTime.fromFormat(text, fmt);
|
||||
if ((0, DateTime_1.validDateTime)(dt)) {
|
||||
return this.fromDateTime(dt, text);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// These are all formats I've seen in the wild from exiftool's output.
|
||||
// More iterations might make sense, like "d MMM, y" or "MMM d, y", but I
|
||||
// want to be constrained in what I consider a valid date to lessen the
|
||||
// chance of misinterpreting a given value.
|
||||
static fromExifStrict(text) {
|
||||
return StrictExifRE.test((0, String_1.toS)(text).trim())
|
||||
? this.fromPatterns(text, ["y:MM:dd", "y-MM-dd", "y:M:d"])
|
||||
: undefined;
|
||||
}
|
||||
static fromExifLoose(text) {
|
||||
// Unfortunately, Luxon parses "00" and "01" as _today_. So if we don't
|
||||
// three non-blank strings parts, reject.
|
||||
return LooseExifRE.test((0, String_1.toS)(text).trim())
|
||||
? this.fromPatterns(text, ["MMM d y", "MMMM d y"])
|
||||
: undefined;
|
||||
}
|
||||
static fromEXIF(text) {
|
||||
return (0, Maybe_1.firstDefinedThunk)([
|
||||
() => this.fromExifStrict(text),
|
||||
() => this.fromExifLoose(text),
|
||||
]);
|
||||
}
|
||||
static fromDateTime(dt, rawValue) {
|
||||
return (0, DateTime_1.validDateTime)(dt)
|
||||
? new ExifDate(dt.year, dt.month, dt.day, rawValue)
|
||||
: undefined;
|
||||
}
|
||||
constructor(year, // full year (probably 2019-ish, but maybe Japanese 30-ish). See https://ericasadun.com/2018/12/25/iso-8601-yyyy-yyyy-and-why-your-year-may-be-wrong/
|
||||
month, // 1-12, (no crazy 0-11 nonsense from Date!)
|
||||
day, // 1-31
|
||||
rawValue) {
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
this.rawValue = rawValue;
|
||||
}
|
||||
toDate() {
|
||||
return new Date(this.year, this.month - 1, this.day);
|
||||
}
|
||||
/**
|
||||
* @param deltaMs defaults to 12 hours, so toMillis() is in the middle of the day.
|
||||
*
|
||||
* @return the epoch milliseconds for this day in UTC, plus `deltaMs` milliseconds.
|
||||
*/
|
||||
toMillis(deltaMs = 12 * DateTime_1.HourMs) {
|
||||
return this.toDate().getTime() + deltaMs;
|
||||
}
|
||||
toISOString() {
|
||||
return this.toString("-");
|
||||
}
|
||||
toExifString() {
|
||||
return this.toString(":");
|
||||
}
|
||||
toString(sep = "-") {
|
||||
return `${this.year}${sep}${(0, String_1.pad2)(this.month, this.day).join(sep)}`;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
_ctor: "ExifDate",
|
||||
year: this.year,
|
||||
month: this.month,
|
||||
day: this.day,
|
||||
rawValue: this.rawValue,
|
||||
};
|
||||
}
|
||||
static fromJSON(json) {
|
||||
return new ExifDate(json.year, json.month, json.day, json.rawValue);
|
||||
}
|
||||
}
|
||||
exports.ExifDate = ExifDate;
|
||||
//# sourceMappingURL=ExifDate.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifDate.js","sourceRoot":"","sources":["../src/ExifDate.ts"],"names":[],"mappings":"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"}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
import { DateTime, DateTimeJSOptions, DurationLike, ToISOTimeOptions, Zone, ZoneOptions } from "luxon";
|
||||
import { Maybe } from "./Maybe";
|
||||
/**
|
||||
* Encapsulates encoding and decoding EXIF date and time strings,
|
||||
* along with timezone handling functionality.
|
||||
*
|
||||
* Key features:
|
||||
* - Parses datetime strings in various formats (EXIF strict/loose, ISO)
|
||||
* - Supports timezone inference, conversion, and matching between dates
|
||||
* - Preserves original string representations when available
|
||||
* - Provides conversion to/from multiple datetime formats (Luxon DateTime, JS
|
||||
* Date, ISO strings)
|
||||
* - Supports serialization/deserialization via JSON (see
|
||||
* {@link ExifDateTime.fromJSON})
|
||||
*
|
||||
* EXIF datetime strings don't typically include timezone information. This
|
||||
* class provides mechanisms to associate timezone data from other EXIF tags
|
||||
* (like GPS position or timezone offset), and distinguishes between explicitly
|
||||
* set and inferred timezone information.
|
||||
*/
|
||||
export declare class ExifDateTime {
|
||||
#private;
|
||||
readonly year: number;
|
||||
readonly month: number;
|
||||
readonly day: number;
|
||||
readonly hour: number;
|
||||
readonly minute: number;
|
||||
readonly second: number;
|
||||
readonly millisecond?: number | undefined;
|
||||
readonly tzoffsetMinutes?: number | undefined;
|
||||
readonly rawValue?: string | undefined;
|
||||
readonly zoneName?: string | undefined;
|
||||
readonly inferredZone?: boolean | undefined;
|
||||
static from(exifOrIso: Maybe<string | ExifDateTime>, defaultZone?: Maybe<string>): Maybe<ExifDateTime>;
|
||||
static fromISO(iso: string, defaultZone?: Maybe<string>): Maybe<ExifDateTime>;
|
||||
/**
|
||||
* Try to parse a date-time string from EXIF. If there is not both a date
|
||||
* and a time component, returns `undefined`.
|
||||
*
|
||||
* @param text from EXIF metadata
|
||||
* @param defaultZone a "zone name" to use as a backstop, or default, if
|
||||
* `text` doesn't specify a zone. This may be IANA-formatted, like
|
||||
* "America/Los_Angeles", or an offset, like "UTC-3". See
|
||||
* `offsetMinutesToZoneName`.
|
||||
*/
|
||||
static fromEXIF(text: string, defaultZone?: Maybe<string>): Maybe<ExifDateTime>;
|
||||
/**
|
||||
* Parse the given date-time string, EXIF-formatted.
|
||||
*
|
||||
* @param text from EXIF metadata, in `y:M:d H:m:s` format (with optional
|
||||
* sub-seconds and/or timezone)
|
||||
|
||||
* @param defaultZone a "zone name" to use as a backstop, or default, if
|
||||
* `text` doesn't specify a zone. This may be IANA-formatted, like
|
||||
* "America/Los_Angeles", or an offset, like "UTC-3". See
|
||||
* `offsetMinutesToZoneName`.
|
||||
*/
|
||||
static fromExifStrict(text: unknown, defaultZone?: Maybe<string>): Maybe<ExifDateTime>;
|
||||
static fromExifLoose(text: unknown, defaultZone?: Maybe<string>): Maybe<ExifDateTime>;
|
||||
static fromDateTime(dt: Maybe<DateTime>, opts?: {
|
||||
rawValue?: Maybe<string>;
|
||||
unsetMilliseconds?: boolean;
|
||||
inferredZone?: Maybe<boolean>;
|
||||
}): Maybe<ExifDateTime>;
|
||||
/**
|
||||
* Create an ExifDateTime from a number of milliseconds since the epoch
|
||||
* (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
|
||||
*
|
||||
* @param millis - a number of milliseconds since 1970 UTC
|
||||
*
|
||||
* @param options.rawValue - the original parsed string input
|
||||
* @param options.zone - the zone to place the DateTime into. Defaults to 'local'.
|
||||
* @param options.locale - a locale to set on the resulting DateTime instance
|
||||
* @param options.outputCalendar - the output calendar to set on the resulting DateTime instance
|
||||
* @param options.numberingSystem - the numbering system to set on the resulting DateTime instance
|
||||
*/
|
||||
static fromMillis(millis: number, options?: DateTimeJSOptions & {
|
||||
rawValue?: string;
|
||||
}): ExifDateTime;
|
||||
static now(opts?: DateTimeJSOptions & {
|
||||
rawValue?: string;
|
||||
}): ExifDateTime;
|
||||
readonly zone: Maybe<string>;
|
||||
constructor(year: number, month: number, day: number, hour: number, minute: number, second: number, millisecond?: number | undefined, tzoffsetMinutes?: number | undefined, rawValue?: string | undefined, zoneName?: string | undefined, inferredZone?: boolean | undefined);
|
||||
get millis(): number | undefined;
|
||||
get hasZone(): boolean;
|
||||
get unsetMilliseconds(): boolean;
|
||||
setZone(zone: string | Zone, opts?: ZoneOptions & {
|
||||
inferredZone?: boolean;
|
||||
}): Maybe<ExifDateTime>;
|
||||
/**
|
||||
* CAUTION: This instance will inherit the system timezone if this instance
|
||||
* has an unset zone (as Luxon doesn't support "unset" timezones)
|
||||
*/
|
||||
toDateTime(overrideZone?: Maybe<string>): DateTime;
|
||||
toEpochSeconds(overrideZone?: Maybe<string>): number;
|
||||
toDate(): Date;
|
||||
toISOString(options?: ToISOTimeOptions): Maybe<string>;
|
||||
toExifString(): string;
|
||||
toString(): Maybe<string>;
|
||||
/**
|
||||
* @return the epoch milliseconds of this
|
||||
*/
|
||||
toMillis(): number;
|
||||
get isValid(): boolean;
|
||||
toJSON(): {
|
||||
_ctor: string;
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
hour: number;
|
||||
minute: number;
|
||||
second: number;
|
||||
millisecond: number | undefined;
|
||||
tzoffsetMinutes: number | undefined;
|
||||
rawValue: string | undefined;
|
||||
zoneName: string | undefined;
|
||||
inferredZone: boolean | undefined;
|
||||
};
|
||||
/**
|
||||
* @return a new ExifDateTime from the given JSON. Note that this instance **may not be valid**.
|
||||
*/
|
||||
static fromJSON(json: Omit<ReturnType<ExifDateTime["toJSON"]>, "_ctor">): ExifDateTime;
|
||||
maybeMatchZone(target: ExifDateTime, maxDeltaMs?: number): Maybe<ExifDateTime>;
|
||||
private ifClose;
|
||||
plus(duration: DurationLike): Maybe<ExifDateTime>;
|
||||
}
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifDateTime = void 0;
|
||||
const luxon_1 = require("luxon");
|
||||
const DateTime_1 = require("./DateTime");
|
||||
const Maybe_1 = require("./Maybe");
|
||||
const Object_1 = require("./Object");
|
||||
const String_1 = require("./String");
|
||||
const TimeParsing_1 = require("./TimeParsing");
|
||||
const Timezones_1 = require("./Timezones");
|
||||
/**
|
||||
* Encapsulates encoding and decoding EXIF date and time strings,
|
||||
* along with timezone handling functionality.
|
||||
*
|
||||
* Key features:
|
||||
* - Parses datetime strings in various formats (EXIF strict/loose, ISO)
|
||||
* - Supports timezone inference, conversion, and matching between dates
|
||||
* - Preserves original string representations when available
|
||||
* - Provides conversion to/from multiple datetime formats (Luxon DateTime, JS
|
||||
* Date, ISO strings)
|
||||
* - Supports serialization/deserialization via JSON (see
|
||||
* {@link ExifDateTime.fromJSON})
|
||||
*
|
||||
* EXIF datetime strings don't typically include timezone information. This
|
||||
* class provides mechanisms to associate timezone data from other EXIF tags
|
||||
* (like GPS position or timezone offset), and distinguishes between explicitly
|
||||
* set and inferred timezone information.
|
||||
*/
|
||||
class ExifDateTime {
|
||||
year;
|
||||
month;
|
||||
day;
|
||||
hour;
|
||||
minute;
|
||||
second;
|
||||
millisecond;
|
||||
tzoffsetMinutes;
|
||||
rawValue;
|
||||
zoneName;
|
||||
inferredZone;
|
||||
static from(exifOrIso, defaultZone) {
|
||||
return exifOrIso instanceof ExifDateTime
|
||||
? exifOrIso // already an ExifDateTime
|
||||
: (0, String_1.blank)(exifOrIso)
|
||||
? undefined // in order of strictness:
|
||||
: (this.fromExifStrict(exifOrIso, defaultZone) ??
|
||||
this.fromISO(exifOrIso, defaultZone) ??
|
||||
this.fromExifLoose(exifOrIso, defaultZone));
|
||||
}
|
||||
static fromISO(iso, defaultZone) {
|
||||
if ((0, String_1.blank)(iso) || null != iso.match(/^\d+$/))
|
||||
return undefined;
|
||||
// Unfortunately, DateTime.fromISO() is happy to parse a date with no time,
|
||||
// so we have to do this ourselves:
|
||||
return this.#fromPatterns(iso, (0, TimeParsing_1.timeFormats)({
|
||||
formatPrefixes: ["y-MM-dd'T'", "y-MM-dd ", "y-M-d "],
|
||||
defaultZone,
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Try to parse a date-time string from EXIF. If there is not both a date
|
||||
* and a time component, returns `undefined`.
|
||||
*
|
||||
* @param text from EXIF metadata
|
||||
* @param defaultZone a "zone name" to use as a backstop, or default, if
|
||||
* `text` doesn't specify a zone. This may be IANA-formatted, like
|
||||
* "America/Los_Angeles", or an offset, like "UTC-3". See
|
||||
* `offsetMinutesToZoneName`.
|
||||
*/
|
||||
static fromEXIF(text, defaultZone) {
|
||||
if ((0, String_1.blank)(text))
|
||||
return undefined;
|
||||
return (
|
||||
// .fromExifStrict() uses .fromISO() as a backstop
|
||||
this.fromExifStrict(text, defaultZone) ??
|
||||
this.fromExifLoose(text, defaultZone));
|
||||
}
|
||||
static #fromPatterns(text, fmts) {
|
||||
const result = (0, TimeParsing_1.parseDateTime)(text, fmts);
|
||||
return result == null
|
||||
? undefined
|
||||
: ExifDateTime.fromDateTime(result.dt, {
|
||||
rawValue: text,
|
||||
unsetMilliseconds: result.unsetMilliseconds,
|
||||
inferredZone: result.inferredZone,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Parse the given date-time string, EXIF-formatted.
|
||||
*
|
||||
* @param text from EXIF metadata, in `y:M:d H:m:s` format (with optional
|
||||
* sub-seconds and/or timezone)
|
||||
|
||||
* @param defaultZone a "zone name" to use as a backstop, or default, if
|
||||
* `text` doesn't specify a zone. This may be IANA-formatted, like
|
||||
* "America/Los_Angeles", or an offset, like "UTC-3". See
|
||||
* `offsetMinutesToZoneName`.
|
||||
*/
|
||||
static fromExifStrict(text, defaultZone) {
|
||||
if ((0, String_1.blank)(text) || !(0, String_1.isString)(text))
|
||||
return undefined;
|
||||
return (this.#fromPatterns(text, (0, TimeParsing_1.timeFormats)({ formatPrefixes: ["y:MM:dd ", "y:M:d "], defaultZone })) ??
|
||||
// Not found yet? Maybe it's in ISO format? See
|
||||
// https://github.com/photostructure/exiftool-vendored.js/issues/71
|
||||
this.fromISO(text, defaultZone));
|
||||
}
|
||||
static *#looseExifFormats(defaultZone) {
|
||||
// The following are from actual datestamps seen in the wild (!!)
|
||||
const formats = [
|
||||
"MMM d y HH:mm:ss",
|
||||
"MMM d y, HH:mm:ss",
|
||||
// Thu Oct 13 00:12:27 2016:
|
||||
"ccc MMM d HH:mm:ss y",
|
||||
];
|
||||
const zone = (0, String_1.notBlank)(defaultZone) ? defaultZone : Timezones_1.UnsetZone;
|
||||
for (const fmt of formats) {
|
||||
yield { fmt: fmt, zone, inferredZone: true };
|
||||
}
|
||||
}
|
||||
static fromExifLoose(text, defaultZone) {
|
||||
return (0, String_1.blank)(text) || !(0, String_1.isString)(text)
|
||||
? undefined
|
||||
: this.#fromPatterns(text, this.#looseExifFormats(defaultZone));
|
||||
}
|
||||
static fromDateTime(dt, opts) {
|
||||
if (dt == null || !dt.isValid || dt.year === 0 || dt.year === 1) {
|
||||
return undefined;
|
||||
}
|
||||
return new ExifDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.millisecond === 0 && true === opts?.unsetMilliseconds
|
||||
? undefined
|
||||
: dt.millisecond, dt.offset === Timezones_1.UnsetZoneOffsetMinutes ? undefined : dt.offset, opts?.rawValue, dt.zoneName == null || dt.zone?.name === Timezones_1.UnsetZone.name
|
||||
? undefined
|
||||
: dt.zoneName, opts?.inferredZone);
|
||||
}
|
||||
/**
|
||||
* Create an ExifDateTime from a number of milliseconds since the epoch
|
||||
* (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
|
||||
*
|
||||
* @param millis - a number of milliseconds since 1970 UTC
|
||||
*
|
||||
* @param options.rawValue - the original parsed string input
|
||||
* @param options.zone - the zone to place the DateTime into. Defaults to 'local'.
|
||||
* @param options.locale - a locale to set on the resulting DateTime instance
|
||||
* @param options.outputCalendar - the output calendar to set on the resulting DateTime instance
|
||||
* @param options.numberingSystem - the numbering system to set on the resulting DateTime instance
|
||||
*/
|
||||
static fromMillis(millis, options = {}) {
|
||||
if (options.zone == null ||
|
||||
[Timezones_1.UnsetZoneName, Timezones_1.UnsetZone].includes(options.zone)) {
|
||||
delete options.zone;
|
||||
}
|
||||
let dt = luxon_1.DateTime.fromMillis(millis, {
|
||||
...(0, Object_1.omit)(options, "rawValue"),
|
||||
});
|
||||
if (options.zone == null) {
|
||||
dt = dt.setZone(Timezones_1.UnsetZone, { keepLocalTime: true });
|
||||
}
|
||||
// TODO: is there a way to provide an invalid millisecond value?
|
||||
return this.fromDateTime(dt, { rawValue: options.rawValue });
|
||||
}
|
||||
static now(opts = {}) {
|
||||
return this.fromMillis(Date.now(), opts);
|
||||
}
|
||||
#dt;
|
||||
zone;
|
||||
constructor(year, month, day, hour, minute, second, millisecond, tzoffsetMinutes, rawValue, zoneName, inferredZone) {
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
this.hour = hour;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
this.millisecond = millisecond;
|
||||
this.tzoffsetMinutes = tzoffsetMinutes;
|
||||
this.rawValue = rawValue;
|
||||
this.zoneName = zoneName;
|
||||
this.inferredZone = inferredZone;
|
||||
this.zone = (0, Timezones_1.getZoneName)({ zoneName, tzoffsetMinutes });
|
||||
}
|
||||
get millis() {
|
||||
return this.millisecond;
|
||||
}
|
||||
get hasZone() {
|
||||
return this.zone != null;
|
||||
}
|
||||
get unsetMilliseconds() {
|
||||
return this.millisecond == null;
|
||||
}
|
||||
setZone(zone, opts) {
|
||||
const dt = (0, TimeParsing_1.setZone)({
|
||||
zone,
|
||||
src: this.toDateTime(),
|
||||
srcHasZone: this.hasZone,
|
||||
opts,
|
||||
});
|
||||
return ExifDateTime.fromDateTime(dt, {
|
||||
rawValue: this.rawValue,
|
||||
unsetMilliseconds: this.millisecond == null,
|
||||
inferredZone: opts?.inferredZone ?? true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* CAUTION: This instance will inherit the system timezone if this instance
|
||||
* has an unset zone (as Luxon doesn't support "unset" timezones)
|
||||
*/
|
||||
toDateTime(overrideZone) {
|
||||
return (this.#dt ??= luxon_1.DateTime.fromObject({
|
||||
year: this.year,
|
||||
month: this.month,
|
||||
day: this.day,
|
||||
hour: this.hour,
|
||||
minute: this.minute,
|
||||
second: this.second,
|
||||
millisecond: this.millisecond,
|
||||
}, {
|
||||
zone: overrideZone ?? this.zone,
|
||||
}));
|
||||
}
|
||||
toEpochSeconds(overrideZone) {
|
||||
return this.toDateTime(overrideZone).toUnixInteger();
|
||||
}
|
||||
toDate() {
|
||||
return this.toDateTime().toJSDate();
|
||||
}
|
||||
toISOString(options = {}) {
|
||||
return (0, Maybe_1.denull)(this.toDateTime().toISO({
|
||||
suppressMilliseconds: options.suppressMilliseconds ?? this.millisecond == null,
|
||||
includeOffset: this.hasZone && options.includeOffset !== false,
|
||||
}));
|
||||
}
|
||||
toExifString() {
|
||||
return (0, DateTime_1.dateTimeToExif)(this.toDateTime(), {
|
||||
includeOffset: this.hasZone,
|
||||
includeMilliseconds: this.millisecond != null,
|
||||
});
|
||||
}
|
||||
toString() {
|
||||
return this.toISOString();
|
||||
}
|
||||
/**
|
||||
* @return the epoch milliseconds of this
|
||||
*/
|
||||
toMillis() {
|
||||
return this.toDateTime().toMillis();
|
||||
}
|
||||
get isValid() {
|
||||
return this.toDateTime().isValid;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
_ctor: "ExifDateTime", // < magick field used by the JSON parser
|
||||
year: this.year,
|
||||
month: this.month,
|
||||
day: this.day,
|
||||
hour: this.hour,
|
||||
minute: this.minute,
|
||||
second: this.second,
|
||||
millisecond: this.millisecond,
|
||||
tzoffsetMinutes: this.tzoffsetMinutes,
|
||||
rawValue: this.rawValue,
|
||||
zoneName: this.zoneName,
|
||||
inferredZone: this.inferredZone,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @return a new ExifDateTime from the given JSON. Note that this instance **may not be valid**.
|
||||
*/
|
||||
static fromJSON(json) {
|
||||
return new ExifDateTime(json.year, json.month, json.day, json.hour, json.minute, json.second, json.millisecond, json.tzoffsetMinutes, json.rawValue, json.zoneName, json.inferredZone);
|
||||
}
|
||||
maybeMatchZone(target, maxDeltaMs = 14 * DateTime_1.MinuteMs) {
|
||||
const targetZone = target.zone;
|
||||
if (targetZone == null || !target.hasZone)
|
||||
return;
|
||||
return (this.setZone(targetZone, { keepLocalTime: false })?.ifClose(target, maxDeltaMs) ??
|
||||
this.setZone(targetZone, { keepLocalTime: true })?.ifClose(target, maxDeltaMs));
|
||||
}
|
||||
ifClose(target, maxDeltaMs = 14 * DateTime_1.MinuteMs) {
|
||||
const ts = this.toMillis();
|
||||
const targetTs = target.toMillis();
|
||||
return Math.abs(ts - targetTs) <= maxDeltaMs ? this : undefined;
|
||||
}
|
||||
plus(duration) {
|
||||
let dt = this.toDateTime().plus(duration);
|
||||
if (!this.hasZone) {
|
||||
dt = dt.setZone(Timezones_1.UnsetZone, { keepLocalTime: true });
|
||||
}
|
||||
return ExifDateTime.fromDateTime(dt, this);
|
||||
}
|
||||
}
|
||||
exports.ExifDateTime = ExifDateTime;
|
||||
//# sourceMappingURL=ExifDateTime.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifDateTime.js","sourceRoot":"","sources":["../src/ExifDateTime.ts"],"names":[],"mappings":"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 truncated
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { DateTime, Zone, ZoneOptions } from "luxon";
|
||||
import { Maybe } from "./Maybe";
|
||||
/**
|
||||
* Encodes an ExifTime (which may not have a timezone offset)
|
||||
*/
|
||||
export declare class ExifTime {
|
||||
#private;
|
||||
readonly hour: number;
|
||||
readonly minute: number;
|
||||
readonly second: number;
|
||||
readonly millisecond?: number | undefined;
|
||||
readonly rawValue?: string | undefined;
|
||||
readonly inferredZone?: boolean | undefined;
|
||||
static fromEXIF(text: string, defaultZone?: Maybe<string>): Maybe<ExifTime>;
|
||||
static fromDateTime(dt: Maybe<DateTime>, rawValue?: string, zone?: string, inferredZone?: boolean, unsetMilliseconds?: boolean): Maybe<ExifTime>;
|
||||
readonly zone: Maybe<string>;
|
||||
constructor(hour: number, minute: number, second: number, millisecond?: number | undefined, rawValue?: string | undefined, zoneName?: Maybe<string>, inferredZone?: boolean | undefined);
|
||||
toDateTime(): DateTime;
|
||||
/**
|
||||
* Alias for `.millisecond`
|
||||
*/
|
||||
get millis(): number | undefined;
|
||||
get hasZone(): boolean;
|
||||
toString(): string;
|
||||
toISOString(): string;
|
||||
toExifString(): string;
|
||||
setZone(zone: string | Zone, opts?: ZoneOptions): Maybe<ExifTime>;
|
||||
toJSON(): {
|
||||
_ctor: string;
|
||||
hour: number;
|
||||
minute: number;
|
||||
second: number;
|
||||
millisecond: number | undefined;
|
||||
rawValue: string | undefined;
|
||||
zone: Maybe<string>;
|
||||
inferredZone: boolean | undefined;
|
||||
};
|
||||
static fromJSON(json: ReturnType<ExifTime["toJSON"]>): ExifTime;
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifTime = void 0;
|
||||
const luxon_1 = require("luxon");
|
||||
const DateTime_1 = require("./DateTime");
|
||||
const String_1 = require("./String");
|
||||
const TimeParsing_1 = require("./TimeParsing");
|
||||
const Timezones_1 = require("./Timezones");
|
||||
/**
|
||||
* Encodes an ExifTime (which may not have a timezone offset)
|
||||
*/
|
||||
class ExifTime {
|
||||
hour;
|
||||
minute;
|
||||
second;
|
||||
millisecond;
|
||||
rawValue;
|
||||
inferredZone;
|
||||
static fromEXIF(text, defaultZone) {
|
||||
const s = (0, String_1.toS)(text).trim();
|
||||
if (s.length === 0)
|
||||
return;
|
||||
const result = (0, TimeParsing_1.parseDateTime)(text, (0, TimeParsing_1.timeFormats)({ defaultZone }));
|
||||
if (result != null) {
|
||||
return this.fromDateTime(result.dt, text, result.unsetZone ? undefined : (0, Timezones_1.getZoneName)({ zone: result.dt.zone }), result.inferredZone, result.unsetMilliseconds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
static fromDateTime(dt, rawValue, zone, inferredZone, unsetMilliseconds) {
|
||||
return !(0, DateTime_1.validDateTime)(dt)
|
||||
? undefined
|
||||
: new ExifTime(dt.hour, dt.minute, dt.second, unsetMilliseconds ? undefined : dt.millisecond, rawValue, zone, inferredZone);
|
||||
}
|
||||
#dt;
|
||||
#z;
|
||||
zone;
|
||||
constructor(hour, minute, second, millisecond, rawValue, zoneName, inferredZone) {
|
||||
this.hour = hour;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
this.millisecond = millisecond;
|
||||
this.rawValue = rawValue;
|
||||
this.inferredZone = inferredZone;
|
||||
this.zone = (0, Timezones_1.getZoneName)({ zoneName });
|
||||
}
|
||||
toDateTime() {
|
||||
return (this.#dt ??= luxon_1.DateTime.fromObject({
|
||||
hour: this.hour,
|
||||
minute: this.minute,
|
||||
second: this.second,
|
||||
millisecond: this.millisecond,
|
||||
}, {
|
||||
zone: this.zone,
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Alias for `.millisecond`
|
||||
*/
|
||||
get millis() {
|
||||
return this.millisecond;
|
||||
}
|
||||
get hasZone() {
|
||||
return this.zone != null;
|
||||
}
|
||||
#subsec() {
|
||||
return this.millisecond == null ? "" : "." + (0, String_1.pad3)(this.millisecond);
|
||||
}
|
||||
#shortZone() {
|
||||
return (this.#z ??= (0, Timezones_1.zoneToShortOffset)(this.zone));
|
||||
}
|
||||
toString() {
|
||||
return ((0, String_1.pad2)(this.hour, this.minute, this.second).join(":") +
|
||||
this.#subsec() +
|
||||
this.#shortZone());
|
||||
}
|
||||
toISOString() {
|
||||
return this.toString();
|
||||
}
|
||||
toExifString() {
|
||||
return this.toString();
|
||||
}
|
||||
setZone(zone, opts) {
|
||||
const dt = (0, TimeParsing_1.setZone)({
|
||||
zone,
|
||||
src: this.toDateTime(),
|
||||
srcHasZone: this.hasZone,
|
||||
opts,
|
||||
});
|
||||
return ExifTime.fromDateTime(dt, this.rawValue, this.zone, this.inferredZone, this.millisecond == null);
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
_ctor: "ExifTime",
|
||||
hour: this.hour,
|
||||
minute: this.minute,
|
||||
second: this.second,
|
||||
millisecond: this.millisecond,
|
||||
rawValue: this.rawValue,
|
||||
zone: this.zone,
|
||||
inferredZone: this.inferredZone,
|
||||
};
|
||||
}
|
||||
static fromJSON(json) {
|
||||
return new ExifTime(json.hour, json.minute, json.second, json.millisecond, json.rawValue, json.zone, json.inferredZone);
|
||||
}
|
||||
}
|
||||
exports.ExifTime = ExifTime;
|
||||
//# sourceMappingURL=ExifTime.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifTime.js","sourceRoot":"","sources":["../src/ExifTime.ts"],"names":[],"mappings":";;;AAAA,iCAAoD;AACpD,yCAA2C;AAE3C,qCAA2C;AAC3C,+CAAoE;AACpE,2CAA6D;AAE7D;;GAEG;AACH,MAAa,QAAQ;IA0CR;IACA;IACA;IACA;IACA;IAEA;IA/CX,MAAM,CAAC,QAAQ,CAAC,IAAY,EAAE,WAA2B;QACvD,MAAM,CAAC,GAAG,IAAA,YAAG,EAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC3B,MAAM,MAAM,GAAG,IAAA,2BAAa,EAAC,IAAI,EAAE,IAAA,yBAAW,EAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC,YAAY,CACtB,MAAM,CAAC,EAAE,EACT,IAAI,EACJ,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,uBAAW,EAAC,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EACpE,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,iBAAiB,CACzB,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,CAAC,YAAY,CACjB,EAAmB,EACnB,QAAiB,EACjB,IAAa,EACb,YAAsB,EACtB,iBAA2B;QAE3B,OAAO,CAAC,IAAA,wBAAa,EAAC,EAAE,CAAC;YACvB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,QAAQ,CACV,EAAE,CAAC,IAAI,EACP,EAAE,CAAC,MAAM,EACT,EAAE,CAAC,MAAM,EACT,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAC9C,QAAQ,EACR,IAAI,EACJ,YAAY,CACb,CAAC;IACR,CAAC;IAED,GAAG,CAAY;IACf,EAAE,CAAiB;IACV,IAAI,CAAgB;IAE7B,YACW,IAAY,EACZ,MAAc,EACd,MAAc,EACd,WAAoB,EACpB,QAAiB,EAC1B,QAAwB,EACf,YAAsB;QANtB,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAQ;QACd,gBAAW,GAAX,WAAW,CAAS;QACpB,aAAQ,GAAR,QAAQ,CAAS;QAEjB,iBAAY,GAAZ,YAAY,CAAU;QAE/B,IAAI,CAAC,IAAI,GAAG,IAAA,uBAAW,EAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,UAAU;QACR,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,gBAAQ,CAAC,UAAU,CACtC;YACE,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,EACD;YACE,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CACF,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAA,aAAI,EAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACtE,CAAC;IAED,UAAU;QACR,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,IAAA,6BAAiB,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,QAAQ;QACN,OAAO,CACL,IAAA,aAAI,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACnD,IAAI,CAAC,OAAO,EAAE;YACd,IAAI,CAAC,UAAU,EAAE,CAClB,CAAC;IACJ,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,IAAmB,EAAE,IAAkB;QAC7C,MAAM,EAAE,GAAG,IAAA,qBAAO,EAAC;YACjB,IAAI;YACJ,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE;YACtB,UAAU,EAAE,IAAI,CAAC,OAAO;YACxB,IAAI;SACL,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,YAAY,CAC1B,EAAE,EACF,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,WAAW,IAAI,IAAI,CACzB,CAAC;IACJ,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,IAAoC;QAClD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,YAAY,CAClB,CAAC;IACJ,CAAC;CACF;AA9ID,4BA8IC"}
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
import * as bc from "batch-cluster";
|
||||
import { BinaryExtractionTaskOptions } from "./BinaryExtractionTask";
|
||||
import { ContainerDirectoryItem } from "./ContainerDirectoryItem";
|
||||
import { Defined, DefinedOrNullValued } from "./Defined";
|
||||
import { ErrorsAndWarnings } from "./ErrorsAndWarnings";
|
||||
import { ExifToolOptions } from "./ExifToolOptions";
|
||||
import { ExifToolTask, ExifToolTaskOptions } from "./ExifToolTask";
|
||||
import { ExifToolVendoredTags } from "./ExifToolVendoredTags";
|
||||
import { GeolocationTags } from "./GeolocationTags";
|
||||
import { ICCProfileTags } from "./ICCProfileTags";
|
||||
import { IPTCApplicationRecordTags } from "./IPTCApplicationRecordTags";
|
||||
import { ImageDataHashTag } from "./ImageDataHashTag";
|
||||
import { CollectionInfo, KeywordInfoStruct, KeywordStruct, MWGCollectionsTags, MWGKeywordTags } from "./MWGTags";
|
||||
import { Maybe } from "./Maybe";
|
||||
import { Omit } from "./Omit";
|
||||
import { PreviewTag } from "./PreviewTag";
|
||||
import { Json, Literal, RawTags } from "./RawTags";
|
||||
import { ReadTaskOptions } from "./ReadTask";
|
||||
import { ResourceEvent } from "./ResourceEvent";
|
||||
import { ShortcutTags } from "./ShortcutTags";
|
||||
import { Struct } from "./Struct";
|
||||
import { APPTags, CompositeTags, EXIFTags, ExifToolTags, FileTags, FlashPixTags, IPTCTags, JFIFTags, MPFTags, MakerNotesTags, MetaTags, PanasonicRawTags, PhotoshopTags, PrintIMTags, QuickTimeTags, RAFTags, RIFFTags, Tags, XMPTags } from "./Tags";
|
||||
import { Version } from "./Version";
|
||||
import { AdditionalWriteTags, ExpandedDateTags, MutableTags, StructAppendTags, WritableGPSRefs, WriteTags } from "./WriteTags";
|
||||
import { WriteTaskOptions, WriteTaskResult } from "./WriteTask";
|
||||
export { BinaryField } from "./BinaryField";
|
||||
export { CapturedAtTagNames } from "./CapturedAtTagNames";
|
||||
export { DefaultExifToolOptions } from "./DefaultExifToolOptions";
|
||||
export { DefaultExiftoolArgs } from "./DefaultExiftoolArgs";
|
||||
export { DefaultMaxProcs } from "./DefaultMaxProcs";
|
||||
export { ExifDate } from "./ExifDate";
|
||||
export { ExifDateTime } from "./ExifDateTime";
|
||||
export { ExifTime } from "./ExifTime";
|
||||
export { ExifToolTask } from "./ExifToolTask";
|
||||
export { exiftoolPath } from "./ExiftoolPath";
|
||||
export { GeolocationTagNames, isGeolocationTag } from "./GeolocationTags";
|
||||
export { parseJSON } from "./JSON";
|
||||
export { DefaultReadTaskOptions } from "./ReadTask";
|
||||
export { TimezoneOffsetTagnames, UnsetZone, UnsetZoneName, UnsetZoneOffsetMinutes, defaultVideosToUTC, offsetMinutesToZoneName, } from "./Timezones";
|
||||
export { DefaultWriteTaskOptions, WriteTaskOptionFields } from "./WriteTask";
|
||||
export type { APPTags, AdditionalWriteTags, IPTCApplicationRecordTags as ApplicationRecordTags, BinaryExtractionTaskOptions, CollectionInfo, CompositeTags, ContainerDirectoryItem, Defined, DefinedOrNullValued, EXIFTags, ErrorsAndWarnings, ExifToolOptions, ExifToolTags, ExifToolTaskOptions, ExifToolVendoredTags, ExpandedDateTags, FileTags, FlashPixTags, GeolocationTags, ICCProfileTags, IPTCApplicationRecordTags, IPTCTags, ImageDataHashTag, JFIFTags, Json, KeywordInfoStruct, KeywordStruct, Literal, MPFTags, MWGCollectionsTags, MWGKeywordTags, MakerNotesTags, Maybe, MetaTags, MutableTags, Omit, PanasonicRawTags, PhotoshopTags, PrintIMTags, QuickTimeTags, RAFTags, RIFFTags, RawTags, ReadTaskOptions, ResourceEvent, ShortcutTags, Struct, StructAppendTags, Tags, Version, WritableGPSRefs, WriteTags, WriteTaskOptions, WriteTaskResult, XMPTags, };
|
||||
/**
|
||||
* Manages delegating calls to a cluster of ExifTool child processes.
|
||||
*
|
||||
* **NOTE: Instances are expensive!**
|
||||
*
|
||||
* * use either the default exported singleton instance of this class,
|
||||
* {@link exiftool}, or your own singleton
|
||||
*
|
||||
* * make sure you await {@link ExifTool.end} when you're done with an instance
|
||||
* to clean up subprocesses
|
||||
*
|
||||
* * review the {@link ExifToolOptions} for configuration options--the default
|
||||
* values are conservative to avoid overwhelming your system.
|
||||
*
|
||||
* @see https://photostructure.github.io/exiftool-vendored.js/ for more documentation.
|
||||
*/
|
||||
export declare class ExifTool {
|
||||
#private;
|
||||
readonly options: ExifToolOptions;
|
||||
readonly batchCluster: bc.BatchCluster;
|
||||
constructor(options?: Partial<ExifToolOptions>);
|
||||
readonly exiftoolPath: () => Promise<string>;
|
||||
/**
|
||||
* Register life cycle event listeners. Delegates to BatchProcess.
|
||||
*/
|
||||
readonly on: bc.BatchCluster["on"];
|
||||
/**
|
||||
* Unregister life cycle event listeners. Delegates to BatchProcess.
|
||||
*/
|
||||
readonly off: bc.BatchCluster["off"];
|
||||
/**
|
||||
* @return a promise holding the version number of the vendored ExifTool
|
||||
*/
|
||||
version(): Promise<string>;
|
||||
/**
|
||||
* Read the tags in `file`.
|
||||
*
|
||||
* @param file the file to extract metadata tags from
|
||||
*
|
||||
* @param options overrides to the default ExifTool options provided to the
|
||||
* ExifTool constructor.
|
||||
*
|
||||
* @returns A resolved Tags promise. If there are errors during reading, the
|
||||
* `.errors` field will be present.
|
||||
*/
|
||||
read<T extends Tags = Tags>(file: string, options?: ReadTaskOptions): Promise<T>;
|
||||
/**
|
||||
* Read the tags in `file`.
|
||||
*
|
||||
* @param file the file to extract metadata tags from
|
||||
*
|
||||
* @param readArgs any additional ExifTool arguments, like `["-fast"]`,
|
||||
* `["-fast2"]`, `["-g"]`, or `["-api", "largefilesupport=1"]`. Note that
|
||||
* providing a value here will override the `readArgs` array provided to the
|
||||
* ExifTool constructor. **Note that most other arguments will require you to
|
||||
* use `readRaw`.** Note that the default is `["-fast"]`, so if you want
|
||||
* ExifTool to read the entire file for metadata, you should pass an empty
|
||||
* array as the second parameter. See https://exiftool.org/#performance for
|
||||
* more information about `-fast` and `-fast2`.
|
||||
*
|
||||
* @param options overrides to the default ExifTool options provided to the
|
||||
* ExifTool constructor.
|
||||
*
|
||||
* @returns A resolved Tags promise. If there are errors during reading, the
|
||||
* `.errors` field will be present.
|
||||
*
|
||||
* @deprecated use
|
||||
* {@link ExifTool.read(file: string, options?: ReadTaskOptions)} instead
|
||||
* (move `readArgs` into your `options` hash)
|
||||
*/
|
||||
read<T extends Tags = Tags>(file: string, readArgs?: string[], options?: ReadTaskOptions): Promise<T>;
|
||||
/**
|
||||
* Read the tags from `file`, without any post-processing of ExifTool values.
|
||||
*
|
||||
* **You probably want `read`, not this method. READ THE REST OF THIS COMMENT
|
||||
* CAREFULLY.**
|
||||
*
|
||||
* If you want to extract specific tag values from a file, you may want to use
|
||||
* this, but all data validation and inference heuristics provided by `read`
|
||||
* will be skipped.
|
||||
*
|
||||
* Note that performance will be very similar to `read`, and will actually be
|
||||
* worse if you don't include `-fast` or `-fast2` (as the most expensive bit
|
||||
* is the perl interpreter and scanning the file on disk).
|
||||
*
|
||||
* @param args any additional arguments other than the file path. Note that
|
||||
* "-json", and the Windows unicode filename handler flags, "-charset
|
||||
* filename=utf8", will be added automatically.
|
||||
*
|
||||
* @return Note that the return value will be similar to `Tags`, but with no
|
||||
* date, time, or other rich type parsing that you get from `.read()`. The
|
||||
* field values will be `string | number | string[]`.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/44 for
|
||||
* typing details.
|
||||
*/
|
||||
readRaw(file: string, args?: string[]): Promise<RawTags>;
|
||||
/**
|
||||
* Write the given `tags` to `file`.
|
||||
*
|
||||
* @param file an existing file to write `tags` to
|
||||
*
|
||||
* @param tags the tags to write to `file`
|
||||
*
|
||||
* @param options overrides to the default ExifTool options provided to the
|
||||
* ExifTool constructor.
|
||||
*
|
||||
* @return Either the promise will be resolved if the tags are written to
|
||||
* successfully, or the promise will be rejected if there are errors or
|
||||
* warnings.
|
||||
*
|
||||
* @see https://exiftool.org/exiftool_pod.html#overwrite_original
|
||||
*/
|
||||
write(file: string, tags: WriteTags, options?: WriteTaskOptions): Promise<WriteTaskResult>;
|
||||
/**
|
||||
* @param file an existing file to write `tags` to
|
||||
*
|
||||
* @param tags the tags to write to `file`.
|
||||
*
|
||||
* @param writeArgs any additional ExifTool arguments, like `-n`, or
|
||||
* `-overwrite_original`.
|
||||
*
|
||||
* @param options overrides to the default ExifTool options provided to the
|
||||
* ExifTool constructor.
|
||||
*
|
||||
* @returns Either the promise will be resolved if the tags are written to
|
||||
* successfully, or the promise will be rejected if there are errors or
|
||||
* warnings.
|
||||
*
|
||||
* @see https://exiftool.org/exiftool_pod.html#overwrite_original
|
||||
*
|
||||
* @deprecated use
|
||||
* {@link ExifTool.write(file: string, tags: WriteTags, options?: WriteTaskOptions)}
|
||||
* instead: move `writeArgs` into your `options` hash.
|
||||
*/
|
||||
write(file: string, tags: WriteTags, writeArgs?: string[], options?: WriteTaskOptions): Promise<WriteTaskResult>;
|
||||
/**
|
||||
* This will strip `file` of all metadata tags. The original file (with the
|
||||
* name `${FILENAME}_original`) will be retained. Note that some tags, like
|
||||
* stat information and image dimensions, are intrinsic to the file and will
|
||||
* continue to exist if you re-`read` the file.
|
||||
*
|
||||
* @param {string} file the file to strip of metadata
|
||||
*
|
||||
* @param {(keyof Tags | string)[]} opts.retain optional. If provided, this is
|
||||
* a list of metadata keys to **not** delete.
|
||||
*/
|
||||
deleteAllTags(file: string, opts?: {
|
||||
retain?: (keyof Tags | string)[];
|
||||
} & Partial<ExifToolTaskOptions>): Promise<WriteTaskResult>;
|
||||
/**
|
||||
* Extract the low-resolution thumbnail in `path/to/image.jpg` and write it to
|
||||
* `path/to/thumbnail.jpg`.
|
||||
*
|
||||
* Note that these images can be less than .1 megapixels in size.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written
|
||||
*/
|
||||
extractThumbnail(imageFile: string, thumbnailFile: string, opts?: BinaryExtractionTaskOptions): Promise<void>;
|
||||
/**
|
||||
* Extract the "preview" image in `path/to/image.jpg` and write it to
|
||||
* `path/to/preview.jpg`.
|
||||
*
|
||||
* The size of these images varies widely, and is present in dSLR images.
|
||||
* Canon, Fuji, Olympus, and Sony use this tag.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written
|
||||
*/
|
||||
extractPreview(imageFile: string, previewFile: string, opts?: BinaryExtractionTaskOptions): Promise<void>;
|
||||
/**
|
||||
* Extract the "JpgFromRaw" image in `path/to/image.jpg` and write it to
|
||||
* `path/to/fromRaw.jpg`.
|
||||
*
|
||||
* This size of these images varies widely, and is not present in all RAW
|
||||
* images. Nikon and Panasonic use this tag.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written.
|
||||
*/
|
||||
extractJpgFromRaw(imageFile: string, outputFile: string, opts?: BinaryExtractionTaskOptions): Promise<void>;
|
||||
/**
|
||||
* Extract a given binary value from "tagname" tag associated to
|
||||
* `path/to/image.jpg` and write it to `dest` (which cannot exist and whose
|
||||
* directory must already exist).
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the binary output not be written to `dest`.
|
||||
*/
|
||||
extractBinaryTag(tagname: string, src: string, dest: string, opts?: BinaryExtractionTaskOptions): Promise<void>;
|
||||
/**
|
||||
* Extract a given binary value from "tagname" tag associated to
|
||||
* `path/to/image.jpg` as a `Buffer`. This has the advantage of not writing to
|
||||
* a file, but if the payload associated to `tagname` is large, this can cause
|
||||
* out-of-memory errors.
|
||||
*
|
||||
* @return a `Promise<Buffer>`
|
||||
*
|
||||
* @throws if the file or tag is missing.
|
||||
*/
|
||||
extractBinaryTagToBuffer(tagname: PreviewTag, imageFile: string, opts?: ExifToolTaskOptions): Promise<Buffer>;
|
||||
/**
|
||||
* Attempt to fix metadata problems in JPEG images by deleting all metadata
|
||||
* and rebuilding from scratch. After repairing an image you should be able to
|
||||
* write to it without errors, but some metadata from the original image may
|
||||
* be lost in the process.
|
||||
*
|
||||
* This should only be applied as a last resort to images whose metadata is
|
||||
* not readable via {@link ExifTool.read}.
|
||||
*
|
||||
* @see https://exiftool.org/faq.html#Q20
|
||||
*
|
||||
* @param {string} inputFile the path to the problematic image
|
||||
* @param {string} outputFile the path to write the repaired image
|
||||
* @param {boolean} opts.allowMakerNoteRepair if there are problems with MakerNote
|
||||
* tags, allow ExifTool to apply heuristics to recover corrupt tags. See
|
||||
* exiftool's `-F` flag.
|
||||
* @return {Promise<void>} resolved after the outputFile has been written.
|
||||
*/
|
||||
rewriteAllTags(inputFile: string, outputFile: string, opts?: {
|
||||
allowMakerNoteRepair?: boolean;
|
||||
} & ExifToolTaskOptions): Promise<void>;
|
||||
/**
|
||||
* Shut down running ExifTool child processes. No subsequent requests will be
|
||||
* accepted.
|
||||
*
|
||||
* This may need to be called in `after` or `finally` clauses in tests or
|
||||
* scripts for them to exit cleanly.
|
||||
*/
|
||||
end(gracefully?: boolean): Promise<void>;
|
||||
/**
|
||||
* @return true if `.end()` has been invoked
|
||||
*/
|
||||
get ended(): boolean;
|
||||
/**
|
||||
* Most users will not need to use `enqueueTask` directly. This method
|
||||
* supports submitting custom `BatchCluster` tasks.
|
||||
*
|
||||
* @param task is a thunk to support retries by providing new instances on retries.
|
||||
*
|
||||
* @see BinaryExtractionTask for an example task implementation
|
||||
*/
|
||||
enqueueTask<T>(task: () => ExifToolTask<T>, retriable?: boolean): Promise<T>;
|
||||
/**
|
||||
* @return the currently running ExifTool processes. Note that on Windows,
|
||||
* these are only the process IDs of the directly-spawned ExifTool wrapper,
|
||||
* and not the actual perl vm. This should only really be relevant for
|
||||
* integration tests that verify processes are cleaned up properly.
|
||||
*/
|
||||
get pids(): number[];
|
||||
/**
|
||||
* @return the number of pending (not currently worked on) tasks
|
||||
*/
|
||||
get pendingTasks(): number;
|
||||
/**
|
||||
* @return the total number of child processes created by this instance
|
||||
*/
|
||||
get spawnedProcs(): number;
|
||||
/**
|
||||
* @return the current number of child processes currently servicing tasks
|
||||
*/
|
||||
get busyProcs(): number;
|
||||
/**
|
||||
* @return report why child processes were recycled
|
||||
*/
|
||||
childEndCounts(): {
|
||||
startError: number;
|
||||
broken: number;
|
||||
closed: number;
|
||||
ending: number;
|
||||
ended: number;
|
||||
idle: number;
|
||||
old: number;
|
||||
"proc.close": number;
|
||||
"proc.disconnect": number;
|
||||
"proc.error": number;
|
||||
"proc.exit": number;
|
||||
"stderr.error": number;
|
||||
stderr: number;
|
||||
"stdin.error": number;
|
||||
"stdout.error": number;
|
||||
timeout: number;
|
||||
tooMany: number;
|
||||
unhealthy: number;
|
||||
worn: number;
|
||||
};
|
||||
/**
|
||||
* Shut down any currently-running child processes. New child processes will
|
||||
* be started automatically to handle new tasks.
|
||||
*/
|
||||
closeChildProcesses(gracefully?: boolean): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Use this singleton rather than instantiating new {@link ExifTool} instances
|
||||
* in order to leverage a single running ExifTool process.
|
||||
*
|
||||
* As of v3.0, its {@link ExifToolOptions.maxProcs} is set to the number of
|
||||
* CPUs on the current system; no more than `maxProcs` instances of `exiftool`
|
||||
* will be spawned. You may want to experiment with smaller or larger values
|
||||
* for `maxProcs`, depending on CPU and disk speed of your system and
|
||||
* performance tradeoffs.
|
||||
*
|
||||
* Note that each child process consumes between 10 and 50 MB of RAM. If you
|
||||
* have limited system resources you may want to use a smaller `maxProcs`
|
||||
* value.
|
||||
*
|
||||
* See the source of {@link DefaultExifToolOptions} for more details about how
|
||||
* this instance is configured.
|
||||
*/
|
||||
export declare const exiftool: ExifTool;
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.exiftool = exports.ExifTool = exports.WriteTaskOptionFields = exports.DefaultWriteTaskOptions = exports.offsetMinutesToZoneName = exports.defaultVideosToUTC = exports.UnsetZoneOffsetMinutes = exports.UnsetZoneName = exports.UnsetZone = exports.TimezoneOffsetTagnames = exports.DefaultReadTaskOptions = exports.parseJSON = exports.isGeolocationTag = exports.GeolocationTagNames = exports.exiftoolPath = exports.ExifToolTask = exports.ExifTime = exports.ExifDateTime = exports.ExifDate = exports.DefaultMaxProcs = exports.DefaultExiftoolArgs = exports.DefaultExifToolOptions = exports.CapturedAtTagNames = exports.BinaryField = void 0;
|
||||
const bc = __importStar(require("batch-cluster"));
|
||||
const _cp = __importStar(require("node:child_process"));
|
||||
const _fs = __importStar(require("node:fs"));
|
||||
const node_process_1 = __importDefault(require("node:process"));
|
||||
const Array_1 = require("./Array");
|
||||
const AsyncRetry_1 = require("./AsyncRetry");
|
||||
const BinaryExtractionTask_1 = require("./BinaryExtractionTask");
|
||||
const BinaryToBufferTask_1 = require("./BinaryToBufferTask");
|
||||
const DefaultExifToolOptions_1 = require("./DefaultExifToolOptions");
|
||||
const DeleteAllTagsArgs_1 = require("./DeleteAllTagsArgs");
|
||||
const ExifToolOptions_1 = require("./ExifToolOptions");
|
||||
const ExiftoolPath_1 = require("./ExiftoolPath");
|
||||
const IsWin32_1 = require("./IsWin32");
|
||||
const Lazy_1 = require("./Lazy");
|
||||
const Object_1 = require("./Object");
|
||||
const Pick_1 = require("./Pick");
|
||||
const ReadRawTask_1 = require("./ReadRawTask");
|
||||
const ReadTask_1 = require("./ReadTask");
|
||||
const RewriteAllTagsTask_1 = require("./RewriteAllTagsTask");
|
||||
const String_1 = require("./String");
|
||||
const VersionTask_1 = require("./VersionTask");
|
||||
const Which_1 = require("./Which");
|
||||
const WriteTask_1 = require("./WriteTask");
|
||||
var BinaryField_1 = require("./BinaryField");
|
||||
Object.defineProperty(exports, "BinaryField", { enumerable: true, get: function () { return BinaryField_1.BinaryField; } });
|
||||
var CapturedAtTagNames_1 = require("./CapturedAtTagNames");
|
||||
Object.defineProperty(exports, "CapturedAtTagNames", { enumerable: true, get: function () { return CapturedAtTagNames_1.CapturedAtTagNames; } });
|
||||
var DefaultExifToolOptions_2 = require("./DefaultExifToolOptions");
|
||||
Object.defineProperty(exports, "DefaultExifToolOptions", { enumerable: true, get: function () { return DefaultExifToolOptions_2.DefaultExifToolOptions; } });
|
||||
var DefaultExiftoolArgs_1 = require("./DefaultExiftoolArgs");
|
||||
Object.defineProperty(exports, "DefaultExiftoolArgs", { enumerable: true, get: function () { return DefaultExiftoolArgs_1.DefaultExiftoolArgs; } });
|
||||
var DefaultMaxProcs_1 = require("./DefaultMaxProcs");
|
||||
Object.defineProperty(exports, "DefaultMaxProcs", { enumerable: true, get: function () { return DefaultMaxProcs_1.DefaultMaxProcs; } });
|
||||
var ExifDate_1 = require("./ExifDate");
|
||||
Object.defineProperty(exports, "ExifDate", { enumerable: true, get: function () { return ExifDate_1.ExifDate; } });
|
||||
var ExifDateTime_1 = require("./ExifDateTime");
|
||||
Object.defineProperty(exports, "ExifDateTime", { enumerable: true, get: function () { return ExifDateTime_1.ExifDateTime; } });
|
||||
var ExifTime_1 = require("./ExifTime");
|
||||
Object.defineProperty(exports, "ExifTime", { enumerable: true, get: function () { return ExifTime_1.ExifTime; } });
|
||||
var ExifToolTask_1 = require("./ExifToolTask");
|
||||
Object.defineProperty(exports, "ExifToolTask", { enumerable: true, get: function () { return ExifToolTask_1.ExifToolTask; } });
|
||||
var ExiftoolPath_2 = require("./ExiftoolPath");
|
||||
Object.defineProperty(exports, "exiftoolPath", { enumerable: true, get: function () { return ExiftoolPath_2.exiftoolPath; } });
|
||||
var GeolocationTags_1 = require("./GeolocationTags");
|
||||
Object.defineProperty(exports, "GeolocationTagNames", { enumerable: true, get: function () { return GeolocationTags_1.GeolocationTagNames; } });
|
||||
Object.defineProperty(exports, "isGeolocationTag", { enumerable: true, get: function () { return GeolocationTags_1.isGeolocationTag; } });
|
||||
var JSON_1 = require("./JSON");
|
||||
Object.defineProperty(exports, "parseJSON", { enumerable: true, get: function () { return JSON_1.parseJSON; } });
|
||||
var ReadTask_2 = require("./ReadTask");
|
||||
Object.defineProperty(exports, "DefaultReadTaskOptions", { enumerable: true, get: function () { return ReadTask_2.DefaultReadTaskOptions; } });
|
||||
var Timezones_1 = require("./Timezones");
|
||||
Object.defineProperty(exports, "TimezoneOffsetTagnames", { enumerable: true, get: function () { return Timezones_1.TimezoneOffsetTagnames; } });
|
||||
Object.defineProperty(exports, "UnsetZone", { enumerable: true, get: function () { return Timezones_1.UnsetZone; } });
|
||||
Object.defineProperty(exports, "UnsetZoneName", { enumerable: true, get: function () { return Timezones_1.UnsetZoneName; } });
|
||||
Object.defineProperty(exports, "UnsetZoneOffsetMinutes", { enumerable: true, get: function () { return Timezones_1.UnsetZoneOffsetMinutes; } });
|
||||
Object.defineProperty(exports, "defaultVideosToUTC", { enumerable: true, get: function () { return Timezones_1.defaultVideosToUTC; } });
|
||||
Object.defineProperty(exports, "offsetMinutesToZoneName", { enumerable: true, get: function () { return Timezones_1.offsetMinutesToZoneName; } });
|
||||
var WriteTask_2 = require("./WriteTask");
|
||||
Object.defineProperty(exports, "DefaultWriteTaskOptions", { enumerable: true, get: function () { return WriteTask_2.DefaultWriteTaskOptions; } });
|
||||
Object.defineProperty(exports, "WriteTaskOptionFields", { enumerable: true, get: function () { return WriteTask_2.WriteTaskOptionFields; } });
|
||||
/**
|
||||
* This is the hardcoded path in the exiftool shebang line
|
||||
*/
|
||||
const PERL = "/usr/bin/perl";
|
||||
/**
|
||||
* Is the #!/usr/bin/perl shebang line in exiftool-vendored.pl going to fail? If
|
||||
* so, we need to find `perl` ourselves, and ignore the shebang line.
|
||||
*/
|
||||
const _ignoreShebang = (0, Lazy_1.lazy)(() => !(0, IsWin32_1.isWin32)() && !_fs.existsSync(PERL));
|
||||
const whichPerl = (0, Lazy_1.lazy)(async () => {
|
||||
const result = await (0, Which_1.which)(PERL);
|
||||
if (result == null) {
|
||||
throw new Error("Perl must be installed. Please add perl to your $PATH and try again.");
|
||||
}
|
||||
return result;
|
||||
});
|
||||
/**
|
||||
* Manages delegating calls to a cluster of ExifTool child processes.
|
||||
*
|
||||
* **NOTE: Instances are expensive!**
|
||||
*
|
||||
* * use either the default exported singleton instance of this class,
|
||||
* {@link exiftool}, or your own singleton
|
||||
*
|
||||
* * make sure you await {@link ExifTool.end} when you're done with an instance
|
||||
* to clean up subprocesses
|
||||
*
|
||||
* * review the {@link ExifToolOptions} for configuration options--the default
|
||||
* values are conservative to avoid overwhelming your system.
|
||||
*
|
||||
* @see https://photostructure.github.io/exiftool-vendored.js/ for more documentation.
|
||||
*/
|
||||
class ExifTool {
|
||||
options;
|
||||
batchCluster;
|
||||
constructor(options = {}) {
|
||||
if (options != null && typeof options !== "object") {
|
||||
throw new Error("Please update caller to the new ExifTool constructor API");
|
||||
}
|
||||
const o = (0, ExifToolOptions_1.handleDeprecatedOptions)({
|
||||
...DefaultExifToolOptions_1.DefaultExifToolOptions,
|
||||
...options,
|
||||
});
|
||||
const ignoreShebang = o.ignoreShebang ?? _ignoreShebang();
|
||||
const env = { ...o.exiftoolEnv, LANG: "C" };
|
||||
if ((0, String_1.notBlank)(node_process_1.default.env.EXIFTOOL_HOME) && (0, String_1.blank)(env.EXIFTOOL_HOME)) {
|
||||
env.EXIFTOOL_HOME = node_process_1.default.env.EXIFTOOL_HOME;
|
||||
}
|
||||
const spawnOpts = {
|
||||
stdio: "pipe",
|
||||
shell: false,
|
||||
detached: false, // < no orphaned exiftool procs, please
|
||||
env,
|
||||
};
|
||||
const processFactory = async () => ignoreShebang
|
||||
? _cp.spawn(await whichPerl(), [await this.exiftoolPath(), ...o.exiftoolArgs], spawnOpts)
|
||||
: _cp.spawn(await this.exiftoolPath(), o.exiftoolArgs, spawnOpts);
|
||||
this.options = {
|
||||
...o,
|
||||
ignoreShebang,
|
||||
processFactory,
|
||||
};
|
||||
this.batchCluster = new bc.BatchCluster(this.options);
|
||||
}
|
||||
exiftoolPath = (0, Lazy_1.lazy)(async () => {
|
||||
const o = await this.options.exiftoolPath;
|
||||
if ((0, String_1.isString)(o) && (0, String_1.notBlank)(o))
|
||||
return o;
|
||||
if ((0, Object_1.isFunction)(o))
|
||||
return o(this.options.logger());
|
||||
return (0, ExiftoolPath_1.exiftoolPath)(this.options.logger());
|
||||
});
|
||||
#taskOptions = (0, Lazy_1.lazy)(() => (0, Pick_1.pick)(this.options, "ignoreMinorErrors"));
|
||||
/**
|
||||
* Register life cycle event listeners. Delegates to BatchProcess.
|
||||
*/
|
||||
on = (event, listener) => this.batchCluster.on(event, listener);
|
||||
/**
|
||||
* Unregister life cycle event listeners. Delegates to BatchProcess.
|
||||
*/
|
||||
off = (event, listener) => this.batchCluster.off(event, listener);
|
||||
/**
|
||||
* @return a promise holding the version number of the vendored ExifTool
|
||||
*/
|
||||
version() {
|
||||
return this.enqueueTask(() => new VersionTask_1.VersionTask(this.options));
|
||||
}
|
||||
read(file, argsOrOptions, options) {
|
||||
const opts = {
|
||||
...(0, Pick_1.pick)(this.options, ...ReadTask_1.ReadTaskOptionFields),
|
||||
...((0, Object_1.isObject)(argsOrOptions) ? argsOrOptions : options),
|
||||
};
|
||||
opts.readArgs =
|
||||
(0, Array_1.ifArray)(argsOrOptions) ?? (0, Array_1.ifArray)(opts.readArgs) ?? this.options.readArgs;
|
||||
return this.enqueueTask(() => ReadTask_1.ReadTask.for(file, opts)); // < no way to know at compile time if we're going to get back a T!
|
||||
}
|
||||
/**
|
||||
* Read the tags from `file`, without any post-processing of ExifTool values.
|
||||
*
|
||||
* **You probably want `read`, not this method. READ THE REST OF THIS COMMENT
|
||||
* CAREFULLY.**
|
||||
*
|
||||
* If you want to extract specific tag values from a file, you may want to use
|
||||
* this, but all data validation and inference heuristics provided by `read`
|
||||
* will be skipped.
|
||||
*
|
||||
* Note that performance will be very similar to `read`, and will actually be
|
||||
* worse if you don't include `-fast` or `-fast2` (as the most expensive bit
|
||||
* is the perl interpreter and scanning the file on disk).
|
||||
*
|
||||
* @param args any additional arguments other than the file path. Note that
|
||||
* "-json", and the Windows unicode filename handler flags, "-charset
|
||||
* filename=utf8", will be added automatically.
|
||||
*
|
||||
* @return Note that the return value will be similar to `Tags`, but with no
|
||||
* date, time, or other rich type parsing that you get from `.read()`. The
|
||||
* field values will be `string | number | string[]`.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/44 for
|
||||
* typing details.
|
||||
*/
|
||||
readRaw(file, args = []) {
|
||||
return this.enqueueTask(() => ReadRawTask_1.ReadRawTask.for(file, args, this.#taskOptions()));
|
||||
}
|
||||
/**
|
||||
* Write the given `tags` to `file`.
|
||||
*
|
||||
* **NOTE: no input validation is done by this library.** ExifTool, however,
|
||||
* is strict about tag names and values in the context of the format of file
|
||||
* being written to.
|
||||
*
|
||||
* @param file an existing file to write `tags` to
|
||||
*
|
||||
* @param tags the tags to write to `file`.
|
||||
*
|
||||
* @param options overrides to the default ExifTool options provided to the
|
||||
* ExifTool constructor.
|
||||
*
|
||||
* @returns Either the promise will be resolved if the tags are written to
|
||||
* successfully, or the promise will be rejected if there are errors or
|
||||
* warnings.
|
||||
*
|
||||
* @see https://exiftool.org/exiftool_pod.html#overwrite_original
|
||||
*/
|
||||
write(file, tags, writeArgsOrOptions, options) {
|
||||
const opts = {
|
||||
...(0, Pick_1.pick)(this.options, ...WriteTask_1.WriteTaskOptionFields),
|
||||
...((0, Object_1.isObject)(writeArgsOrOptions) ? writeArgsOrOptions : options),
|
||||
};
|
||||
opts.writeArgs =
|
||||
(0, Array_1.ifArray)(writeArgsOrOptions) ??
|
||||
(0, Array_1.ifArray)(opts.writeArgs) ??
|
||||
this.options.writeArgs;
|
||||
// don't retry because writes might not be idempotent (e.g. incrementing
|
||||
// timestamps by an hour)
|
||||
const retriable = false;
|
||||
return this.enqueueTask(() => WriteTask_1.WriteTask.for(file, tags, opts), retriable);
|
||||
}
|
||||
/**
|
||||
* This will strip `file` of all metadata tags. The original file (with the
|
||||
* name `${FILENAME}_original`) will be retained. Note that some tags, like
|
||||
* stat information and image dimensions, are intrinsic to the file and will
|
||||
* continue to exist if you re-`read` the file.
|
||||
*
|
||||
* @param {string} file the file to strip of metadata
|
||||
*
|
||||
* @param {(keyof Tags | string)[]} opts.retain optional. If provided, this is
|
||||
* a list of metadata keys to **not** delete.
|
||||
*/
|
||||
deleteAllTags(file, opts) {
|
||||
const writeArgs = [...DeleteAllTagsArgs_1.DeleteAllTagsArgs];
|
||||
for (const ea of opts?.retain ?? []) {
|
||||
writeArgs.push(`-${ea}<${ea}`);
|
||||
}
|
||||
return this.write(file, {}, { ...(0, Object_1.omit)(opts ?? {}, "retain"), writeArgs });
|
||||
}
|
||||
/**
|
||||
* Extract the low-resolution thumbnail in `path/to/image.jpg` and write it to
|
||||
* `path/to/thumbnail.jpg`.
|
||||
*
|
||||
* Note that these images can be less than .1 megapixels in size.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written
|
||||
*/
|
||||
extractThumbnail(imageFile, thumbnailFile, opts) {
|
||||
return this.extractBinaryTag("ThumbnailImage", imageFile, thumbnailFile, opts);
|
||||
}
|
||||
/**
|
||||
* Extract the "preview" image in `path/to/image.jpg` and write it to
|
||||
* `path/to/preview.jpg`.
|
||||
*
|
||||
* The size of these images varies widely, and is present in dSLR images.
|
||||
* Canon, Fuji, Olympus, and Sony use this tag.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written
|
||||
*/
|
||||
extractPreview(imageFile, previewFile, opts) {
|
||||
return this.extractBinaryTag("PreviewImage", imageFile, previewFile, opts);
|
||||
}
|
||||
/**
|
||||
* Extract the "JpgFromRaw" image in `path/to/image.jpg` and write it to
|
||||
* `path/to/fromRaw.jpg`.
|
||||
*
|
||||
* This size of these images varies widely, and is not present in all RAW
|
||||
* images. Nikon and Panasonic use this tag.
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the file could not be read or the output not written.
|
||||
*/
|
||||
extractJpgFromRaw(imageFile, outputFile, opts) {
|
||||
return this.extractBinaryTag("JpgFromRaw", imageFile, outputFile, opts);
|
||||
}
|
||||
/**
|
||||
* Extract a given binary value from "tagname" tag associated to
|
||||
* `path/to/image.jpg` and write it to `dest` (which cannot exist and whose
|
||||
* directory must already exist).
|
||||
*
|
||||
* @return a `Promise<void>`
|
||||
*
|
||||
* @throws if the binary output not be written to `dest`.
|
||||
*/
|
||||
async extractBinaryTag(tagname, src, dest, opts) {
|
||||
// BinaryExtractionTask returns a stringified error if the output indicates
|
||||
// the task should not be retried.
|
||||
const maybeError = await this.enqueueTask(() => BinaryExtractionTask_1.BinaryExtractionTask.for(tagname, src, dest, {
|
||||
...this.#taskOptions(),
|
||||
...opts,
|
||||
}));
|
||||
if (maybeError != null) {
|
||||
throw new Error(maybeError);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Extract a given binary value from "tagname" tag associated to
|
||||
* `path/to/image.jpg` as a `Buffer`. This has the advantage of not writing to
|
||||
* a file, but if the payload associated to `tagname` is large, this can cause
|
||||
* out-of-memory errors.
|
||||
*
|
||||
* @return a `Promise<Buffer>`
|
||||
*
|
||||
* @throws if the file or tag is missing.
|
||||
*/
|
||||
async extractBinaryTagToBuffer(tagname, imageFile, opts) {
|
||||
const result = await this.enqueueTask(() => BinaryToBufferTask_1.BinaryToBufferTask.for(tagname, imageFile, {
|
||||
...this.#taskOptions(),
|
||||
...opts,
|
||||
}));
|
||||
if (Buffer.isBuffer(result)) {
|
||||
return result;
|
||||
}
|
||||
else if (result instanceof Error) {
|
||||
throw result;
|
||||
}
|
||||
else {
|
||||
throw new Error("Unexpected result from BinaryToBufferTask: " + JSON.stringify(result));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Attempt to fix metadata problems in JPEG images by deleting all metadata
|
||||
* and rebuilding from scratch. After repairing an image you should be able to
|
||||
* write to it without errors, but some metadata from the original image may
|
||||
* be lost in the process.
|
||||
*
|
||||
* This should only be applied as a last resort to images whose metadata is
|
||||
* not readable via {@link ExifTool.read}.
|
||||
*
|
||||
* @see https://exiftool.org/faq.html#Q20
|
||||
*
|
||||
* @param {string} inputFile the path to the problematic image
|
||||
* @param {string} outputFile the path to write the repaired image
|
||||
* @param {boolean} opts.allowMakerNoteRepair if there are problems with MakerNote
|
||||
* tags, allow ExifTool to apply heuristics to recover corrupt tags. See
|
||||
* exiftool's `-F` flag.
|
||||
* @return {Promise<void>} resolved after the outputFile has been written.
|
||||
*/
|
||||
rewriteAllTags(inputFile, outputFile, opts) {
|
||||
return this.enqueueTask(() => RewriteAllTagsTask_1.RewriteAllTagsTask.for(inputFile, outputFile, {
|
||||
allowMakerNoteRepair: false,
|
||||
...this.#taskOptions(),
|
||||
...opts,
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Shut down running ExifTool child processes. No subsequent requests will be
|
||||
* accepted.
|
||||
*
|
||||
* This may need to be called in `after` or `finally` clauses in tests or
|
||||
* scripts for them to exit cleanly.
|
||||
*/
|
||||
end(gracefully = true) {
|
||||
return this.batchCluster.end(gracefully).promise;
|
||||
}
|
||||
/**
|
||||
* @return true if `.end()` has been invoked
|
||||
*/
|
||||
get ended() {
|
||||
return this.batchCluster.ended;
|
||||
}
|
||||
// calling whichPerl through this lazy() means we only do that task once per
|
||||
// instance.
|
||||
#checkForPerl = (0, Lazy_1.lazy)(async () => {
|
||||
if (this.options.checkPerl) {
|
||||
await whichPerl(); // < throws if perl is missing
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Most users will not need to use `enqueueTask` directly. This method
|
||||
* supports submitting custom `BatchCluster` tasks.
|
||||
*
|
||||
* @param task is a thunk to support retries by providing new instances on retries.
|
||||
*
|
||||
* @see BinaryExtractionTask for an example task implementation
|
||||
*/
|
||||
enqueueTask(task, retriable = true) {
|
||||
const f = async () => {
|
||||
await this.#checkForPerl();
|
||||
return this.batchCluster.enqueueTask(task());
|
||||
};
|
||||
return retriable ? (0, AsyncRetry_1.retryOnReject)(f, this.options.taskRetries) : f();
|
||||
}
|
||||
/**
|
||||
* @return the currently running ExifTool processes. Note that on Windows,
|
||||
* these are only the process IDs of the directly-spawned ExifTool wrapper,
|
||||
* and not the actual perl vm. This should only really be relevant for
|
||||
* integration tests that verify processes are cleaned up properly.
|
||||
*/
|
||||
get pids() {
|
||||
return this.batchCluster.pids();
|
||||
}
|
||||
/**
|
||||
* @return the number of pending (not currently worked on) tasks
|
||||
*/
|
||||
get pendingTasks() {
|
||||
return this.batchCluster.pendingTaskCount;
|
||||
}
|
||||
/**
|
||||
* @return the total number of child processes created by this instance
|
||||
*/
|
||||
get spawnedProcs() {
|
||||
return this.batchCluster.spawnedProcCount;
|
||||
}
|
||||
/**
|
||||
* @return the current number of child processes currently servicing tasks
|
||||
*/
|
||||
get busyProcs() {
|
||||
return this.batchCluster.busyProcCount;
|
||||
}
|
||||
/**
|
||||
* @return report why child processes were recycled
|
||||
*/
|
||||
childEndCounts() {
|
||||
return this.batchCluster.childEndCounts;
|
||||
}
|
||||
/**
|
||||
* Shut down any currently-running child processes. New child processes will
|
||||
* be started automatically to handle new tasks.
|
||||
*/
|
||||
closeChildProcesses(gracefully = true) {
|
||||
return this.batchCluster.closeChildProcesses(gracefully);
|
||||
}
|
||||
}
|
||||
exports.ExifTool = ExifTool;
|
||||
/**
|
||||
* Use this singleton rather than instantiating new {@link ExifTool} instances
|
||||
* in order to leverage a single running ExifTool process.
|
||||
*
|
||||
* As of v3.0, its {@link ExifToolOptions.maxProcs} is set to the number of
|
||||
* CPUs on the current system; no more than `maxProcs` instances of `exiftool`
|
||||
* will be spawned. You may want to experiment with smaller or larger values
|
||||
* for `maxProcs`, depending on CPU and disk speed of your system and
|
||||
* performance tradeoffs.
|
||||
*
|
||||
* Note that each child process consumes between 10 and 50 MB of RAM. If you
|
||||
* have limited system resources you may want to use a smaller `maxProcs`
|
||||
* value.
|
||||
*
|
||||
* See the source of {@link DefaultExifToolOptions} for more details about how
|
||||
* this instance is configured.
|
||||
*/
|
||||
exports.exiftool = new ExifTool();
|
||||
//# sourceMappingURL=ExifTool.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifTool.js","sourceRoot":"","sources":["../src/ExifTool.ts"],"names":[],"mappings":"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 truncated
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
import * as bc from "batch-cluster";
|
||||
import { geoTz } from "./GeoTz";
|
||||
import { Maybe } from "./Maybe";
|
||||
import { Tags } from "./Tags";
|
||||
/**
|
||||
* Options for the {@link ExifTool} constructor.
|
||||
*
|
||||
* Defaults are defined in {@link DefaultExifToolOptions}.
|
||||
*/
|
||||
export interface ExifToolOptions extends bc.BatchClusterOptions, bc.BatchProcessOptions, bc.ChildProcessFactory {
|
||||
/**
|
||||
* The maximum number of ExifTool child processes to spawn when load merits.
|
||||
*
|
||||
* Defaults to 1/4 the number of CPUs, minimally 1.
|
||||
*/
|
||||
maxProcs: number;
|
||||
/**
|
||||
* The maximum number of requests a given ExifTool process will service
|
||||
* before being retired.
|
||||
*
|
||||
* Defaults to 500, to balance performance with memory usage.
|
||||
*/
|
||||
maxTasksPerProcess: number;
|
||||
/**
|
||||
* Spawning new ExifTool processes must not take longer than
|
||||
* {@link spawnTimeoutMillis} milliseconds before the child process is timed
|
||||
* out and a new attempt is made. Be pessimistic here--windows can regularly
|
||||
* take several seconds to spin up a process, thanks to antivirus
|
||||
* shenanigans. This can't be set to a value less than 100ms.
|
||||
*
|
||||
* Defaults to 30 seconds, to accommodate slow Windows machines.
|
||||
*/
|
||||
spawnTimeoutMillis: number;
|
||||
/**
|
||||
* If requests to ExifTool take longer than this, presume the underlying
|
||||
* process is dead and we should restart the task. This can't be set to a
|
||||
* value less than 10ms, and really should be set to at more than a second
|
||||
* unless `taskRetries` is sufficiently large or all writes will be to a
|
||||
* fast local disk. Defaults to 10 seconds.
|
||||
*/
|
||||
taskTimeoutMillis: number;
|
||||
/**
|
||||
* An interval timer is scheduled to do periodic maintenance of underlying
|
||||
* child processes with this periodicity.
|
||||
*
|
||||
* Defaults to 2 seconds.
|
||||
*/
|
||||
onIdleIntervalMillis: number;
|
||||
/**
|
||||
* The number of times a task can error or timeout and be retried.
|
||||
*
|
||||
* Defaults to 1 (every task gets 2 chances).
|
||||
*/
|
||||
taskRetries: number;
|
||||
/**
|
||||
* Allows for non-standard paths to ExifTool. Defaults to the perl or
|
||||
* windows binaries provided by `exiftool-vendored.pl` or
|
||||
* `exiftool-vendored.exe`.
|
||||
*
|
||||
* This must be the full path to `exiftool`, not just the directory.
|
||||
*/
|
||||
exiftoolPath: string | Promise<string> | ((logger?: bc.Logger) => string | Promise<string>);
|
||||
/**
|
||||
* Args only passed to exiftool on launch. You probably don't need to change
|
||||
* this from the default.
|
||||
*/
|
||||
exiftoolArgs: string[];
|
||||
/**
|
||||
* Environment variables passed to ExifTool (besides `EXIFTOOL_HOME`)
|
||||
*/
|
||||
exiftoolEnv: NodeJS.ProcessEnv;
|
||||
/**
|
||||
* Should ExifTool use MWG (Metadata Working Group) composite tags for
|
||||
* reading and writing tags?
|
||||
*
|
||||
* ExifTool recommends this to be set to true. This defaults to `false` to
|
||||
* maintain consistency with prior versions.
|
||||
*
|
||||
* Note that this can result in many tag value differences from
|
||||
* `ExifTool.read`, and makes `ExifTool.write` write to "synonymous" MWG
|
||||
* tags automatically.
|
||||
*
|
||||
* @see https://exiftool.org/TagNames/MWG.html for details
|
||||
*/
|
||||
useMWG: boolean;
|
||||
/**
|
||||
* Tag names (which can have '*' glob matchers) which you want numeric
|
||||
* values, rather than ExifTool's "Print Conversion."
|
||||
*
|
||||
* If you're using tag values only for human consumption, you may want to
|
||||
* leave this blank.
|
||||
*
|
||||
* The default includes "*Duration*", {@link Tags.GPSAltitude},
|
||||
* {@link Tags.GPSLatitude}, {@link Tags.GPSLongitude},
|
||||
* {@link Tags.GPSPosition}, and {@link Tags.Orientation}.
|
||||
*/
|
||||
numericTags: string[];
|
||||
/**
|
||||
* If defined, ExifTool will attempt to calculate an "ImageDataHash" tag
|
||||
* value with a checksum of image data.
|
||||
*
|
||||
* Note that as of 2022-04-12, ExifTool supports JPEG, TIFF, PNG, CRW, CR3,
|
||||
* MRW, RAF, X3F, IIQ, JP2, JXL, HEIC and AVIF images, MOV/MP4 videos, and
|
||||
* some RIFF-based files such as AVI, WAV and WEBP.
|
||||
*
|
||||
* This defaults to undefined, as it adds ~20ms of overhead to every read
|
||||
*/
|
||||
imageHashType: false | "MD5" | "SHA256" | "SHA512";
|
||||
/**
|
||||
* @deprecated Use `imageHashType` instead.
|
||||
*/
|
||||
includeImageDataMD5: boolean | undefined;
|
||||
/**
|
||||
* Video file dates are assumed to be in UTC, rather than using timezone
|
||||
* inference used in images. To disable this default, set this to false.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/113
|
||||
*/
|
||||
defaultVideosToUTC: boolean;
|
||||
/**
|
||||
* Should we try to backfill timezones for date-times that don't have them?
|
||||
* If set to `true`, and {@link defaultVideosToUTC} is also `true`, we'll
|
||||
* try backfilling timezones for date-times that are UTC, as well.
|
||||
*
|
||||
* Setting this to `false` removes **all** timezone inference--only those
|
||||
* date-times with an explicit offset will have a defined timezone.
|
||||
*
|
||||
* Prior versions of exiftool-vendored would use the file's `.tz` as a
|
||||
* backstop even if this was set to `false`.
|
||||
*
|
||||
* As of version 23, this now defaults to `true`, as it's more likely to be
|
||||
* what people expect.
|
||||
*/
|
||||
backfillTimezones: boolean;
|
||||
/**
|
||||
* We always look at {@link Tags.TimeZone}, {@link Tags.OffsetTime},
|
||||
* {@link Tags.TimeZoneOffset}, {@link Tags.OffsetTimeOriginal},
|
||||
* {@link Tags.OffsetTimeDigitized}, and GPS metadata to infer the timezone.
|
||||
*
|
||||
* If these strategies fail, and this is enabled, we'll try to infer the
|
||||
* timezone from non-UTC datestamps included in the
|
||||
* {@link inferTimezoneFromDatestampTags} value.
|
||||
*
|
||||
* This defaults to false as it both retains prior behavior and means fewer
|
||||
* "fuzzy" heuristics are enabled by default.
|
||||
*/
|
||||
inferTimezoneFromDatestamps: boolean;
|
||||
/**
|
||||
* This is the list of tag names that will be used to infer the timezone as
|
||||
* a backstop, if no explicit timezone is found in metadata. Note that
|
||||
* datestamps with UTC offsets are ignored, as they are frequently
|
||||
* incorrectly set.
|
||||
*
|
||||
* This setting is only in play if {@link inferTimezoneFromDatestamps} has
|
||||
* been overridden to be `true`.
|
||||
*
|
||||
* This defaults to {@link CapturedAtTagNames}
|
||||
*/
|
||||
inferTimezoneFromDatestampTags: (keyof Tags)[];
|
||||
/**
|
||||
* Some cameras (Samsung Galaxy S7, for example) may not always include GPS
|
||||
* metadata in photos if a fix can't be obtained. If this option is true, and
|
||||
* GPS metadata is missing, we'll try to infer the timezone from the
|
||||
* difference of the TimeStamp tag and the first defined tag value from
|
||||
* {@link inferTimezoneFromDatestampTags}.
|
||||
*
|
||||
* This heuristic is pretty sketchy, and used as a last resort. You shouldn't
|
||||
* enable it unless you have to.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/209
|
||||
*/
|
||||
inferTimezoneFromTimeStamp: boolean;
|
||||
/**
|
||||
* Some software uses a GPS position of (0,0) as a synonym for "unset". If
|
||||
* this option is true, and GPSLatitude and GPSLongitude are both 0, then
|
||||
* those values will be returned, but the TZ will not be inferred from that
|
||||
* location.
|
||||
*
|
||||
* If both this and {@link geolocation} are `true`, we will _delete_ the
|
||||
* Geolocation tags from the returned metadata object.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Null_Island
|
||||
*/
|
||||
ignoreZeroZeroLatLon: boolean;
|
||||
/**
|
||||
* Override the default geo-to-timezone lookup service. Note that if
|
||||
{@link geolocation} is enabled, we'll use
|
||||
{@link Tags.GeolocationTimeZone} if it's not blank.
|
||||
*
|
||||
* This defaults to `@photostructure/tz-lookup`, but if you have the
|
||||
* resources, consider using `geo-tz` for more accurate results.
|
||||
*
|
||||
* If your implementation throws an error, `ExifTool` will consider that given
|
||||
* latitude/longitude as invalid.
|
||||
*
|
||||
* Here's a snippet of how to use `geo-tz` instead of `tz-lookup`:
|
||||
*
|
||||
```js
|
||||
const geotz = require("geo-tz")
|
||||
const { ExifTool } = require("exiftool-vendored")
|
||||
const exiftool = new ExifTool({ geoTz: (lat, lon) => geotz.find(lat, lon)[0] })
|
||||
```
|
||||
*
|
||||
* @see https://github.com/photostructure/tz-lookup
|
||||
* @see https://github.com/evansiroky/node-geo-tz/
|
||||
*/
|
||||
geoTz: typeof geoTz;
|
||||
/**
|
||||
* When reading metadata, should we enable ExifTool's geolocation features?
|
||||
* Note that this requires ExifTool version 12.78 or later.
|
||||
*
|
||||
* @see https://exiftool.org/geolocation.html
|
||||
* @see {@link GeolocationTags}
|
||||
*/
|
||||
geolocation: boolean;
|
||||
/**
|
||||
* Should we ignore minor errors when reading metadata?
|
||||
*
|
||||
* This defaults to `true`, as ExifTool can be quite chatty.
|
||||
*/
|
||||
ignoreMinorErrors: boolean;
|
||||
/**
|
||||
* When writing an extracted tag to a file,
|
||||
* this will overwrite an existing file instead of throwing an error.
|
||||
* Enabling this option is equivalent to `-w!` in ExifTool.
|
||||
*
|
||||
* This defaults to `false`.
|
||||
*/
|
||||
forceWrite: boolean;
|
||||
/**
|
||||
* `ExifTool` has a shebang line that assumes a valid `perl` is installed at
|
||||
* `/usr/bin/perl`.
|
||||
*
|
||||
* Some environments may not include a valid `/usr/bin/perl` (like AWS
|
||||
* Lambda), but `perl` may be available in your `PATH` some place else (like
|
||||
* `/opt/bin/perl`), if you pull in a perl layer.
|
||||
*
|
||||
* This will default to `true` in those environments as a workaround in these
|
||||
* situations. Note also that `perl` will be spawned in a sub-shell.
|
||||
*/
|
||||
ignoreShebang: boolean;
|
||||
/**
|
||||
* Should we check for a readable and executable `perl` file in $PATH? This
|
||||
* defaults to false on Windows, and true everywhere else. Set this to false
|
||||
* if you know perl is installed.
|
||||
*/
|
||||
checkPerl: boolean;
|
||||
/**
|
||||
* How should ExifTool handle nested structures?
|
||||
*
|
||||
* Defaults to `1`.
|
||||
*
|
||||
* - `0` = Read/copy flattened tags
|
||||
* - `1` = Read/copy structures
|
||||
* - `2` = Read/copy both flattened and structured tags, but flag flattened
|
||||
* tags as "unsafe" for copying
|
||||
* - `"undef"` = Same as `0` for reading and `2` for copying
|
||||
*
|
||||
* @see https://exiftool.org/struct.html
|
||||
*/
|
||||
struct: "undef" | 0 | 1 | 2;
|
||||
/**
|
||||
* Any additional arguments that should be added by default to all read tasks,
|
||||
* like `["-fast", "-api", "largefilesupport=1"]`. The value provided to the
|
||||
* ExifTool constructor can be overridden in the call to {@link ExifTool.read}
|
||||
*/
|
||||
readArgs: string[];
|
||||
/**
|
||||
* Any additional arguments that should be added by default to all write
|
||||
* tasks, like `["-overwrite_original"]`. The value provided to the ExifTool
|
||||
* constructor can be overridden in the call to {@link ExifTool.write}.
|
||||
*/
|
||||
writeArgs: string[];
|
||||
/**
|
||||
* The TimeZone tag normally represents the offset from UTC.
|
||||
*
|
||||
* Unfortunately, at least for some Nikon cameras, the TimeZone tag **and the
|
||||
* DaylightSavings tag** must be taken into account to find the UTC offset.
|
||||
*
|
||||
* By default, this is a predicate that returns `true` if the `Make` tag is
|
||||
* `Nikon`. If you find other makes and models that need this treatment,
|
||||
* please open a ticket on GitHub with example images or videos and we can
|
||||
* update the default predicate.
|
||||
*
|
||||
* The return value is the number of minutes to adjust the timezone by.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/215
|
||||
*/
|
||||
adjustTimeZoneIfDaylightSavings: (tags: Tags, tz: string) => Maybe<number>;
|
||||
/**
|
||||
* Timezone parsing requires a bunch of heuristics due to hardware and
|
||||
* software companies not following metadata specifications similarly.
|
||||
*
|
||||
* If GPS metadata is trustworthy, set this to `true` to override explicit
|
||||
* values assigned to {@link TimezoneOffsetTagnames}.
|
||||
*
|
||||
* Note that there **are** regions that have had their IANA timezone change
|
||||
* over time--this will result incorrect timezones.
|
||||
*/
|
||||
preferTimezoneInferenceFromGps: boolean;
|
||||
/**
|
||||
* Should ExifTool keep times that are stored as seconds since UTC epoch as
|
||||
* UTC times? If false, ExifTool will use local time instead of UTC/Zulu.
|
||||
*
|
||||
* We default to `true` to ensure we don't unintentionally adopt local
|
||||
* timezones.
|
||||
*
|
||||
* Please note: when trying to validate this option, I **could not find a
|
||||
* single example** that had a unixtime-encoded datetime, so I suspect this is
|
||||
* irrelevant for most use cases and files.
|
||||
*
|
||||
* @see https://exiftool.org/ExifTool.html#KeepUTCTime
|
||||
*/
|
||||
keepUTCTime: boolean;
|
||||
}
|
||||
export declare function handleDeprecatedOptions<T extends Partial<Pick<ExifToolOptions, "includeImageDataMD5" | "imageHashType">>>(options: T): T;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.handleDeprecatedOptions = handleDeprecatedOptions;
|
||||
function handleDeprecatedOptions(options) {
|
||||
if (options.imageHashType == null && options.includeImageDataMD5 != null) {
|
||||
options.imageHashType = options.includeImageDataMD5 ? "MD5" : false;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
//# sourceMappingURL=ExifToolOptions.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifToolOptions.js","sourceRoot":"","sources":["../src/ExifToolOptions.ts"],"names":[],"mappings":";;AAiWA,0DASC;AATD,SAAgB,uBAAuB,CAIrC,OAAU;IACV,IAAI,OAAO,CAAC,aAAa,IAAI,IAAI,IAAI,OAAO,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;QACzE,OAAO,CAAC,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { ExifToolTags } from "./Tags";
|
||||
export declare const ExifToolTagNames: (keyof ExifToolTags)[];
|
||||
/**
|
||||
* Is the given tag name intrinsic to the content of a given file? In other
|
||||
* words, is it not an artifact of a metadata field?
|
||||
*/
|
||||
export declare function isExifToolTag(name: string): name is keyof ExifToolTags;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifToolTagNames = void 0;
|
||||
exports.isExifToolTag = isExifToolTag;
|
||||
const Object_1 = require("./Object");
|
||||
exports.ExifToolTagNames = (0, Object_1.keysOf)({
|
||||
ExifToolVersion: true,
|
||||
SourceFile: true,
|
||||
Error: true,
|
||||
Warning: true,
|
||||
});
|
||||
/**
|
||||
* Is the given tag name intrinsic to the content of a given file? In other
|
||||
* words, is it not an artifact of a metadata field?
|
||||
*/
|
||||
function isExifToolTag(name) {
|
||||
return exports.ExifToolTagNames.includes(name);
|
||||
}
|
||||
//# sourceMappingURL=ExifToolTags.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifToolTags.js","sourceRoot":"","sources":["../src/ExifToolTags.ts"],"names":[],"mappings":";;;AAcA,sCAEC;AAhBD,qCAAkC;AAGrB,QAAA,gBAAgB,GAAG,IAAA,eAAM,EAAe;IACnD,eAAe,EAAE,IAAI;IACrB,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,IAAI;CACd,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,OAAO,wBAAgB,CAAC,QAAQ,CAAC,IAA0B,CAAC,CAAC;AAC/D,CAAC"}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import * as bc from "batch-cluster";
|
||||
import { ExifToolOptions } from "./ExifToolOptions";
|
||||
export type ExifToolTaskOptions = Pick<ExifToolOptions, "ignoreMinorErrors">;
|
||||
export declare abstract class ExifToolTask<T> extends bc.Task<T> {
|
||||
#private;
|
||||
readonly args: string[];
|
||||
readonly options?: ExifToolTaskOptions | undefined;
|
||||
static renderCommand(args: string[], options?: ExifToolTaskOptions): string;
|
||||
readonly errors: string[];
|
||||
readonly warnings: string[];
|
||||
constructor(args: string[], options?: ExifToolTaskOptions | undefined);
|
||||
onStderr(buf: string | Buffer): void;
|
||||
protected abstract parse(input: string, error?: Error): T;
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifToolTask = void 0;
|
||||
const bc = __importStar(require("batch-cluster"));
|
||||
const IsWarning_1 = require("./IsWarning");
|
||||
const String_1 = require("./String");
|
||||
const BadPerlInstallationRE = /Can't locate \S+ in @INC/i;
|
||||
class ExifToolTask extends bc.Task {
|
||||
args;
|
||||
options;
|
||||
static renderCommand(args, options) {
|
||||
const result = args.filter((ea) => !(0, String_1.blank)(ea));
|
||||
if (options?.ignoreMinorErrors === true) {
|
||||
result.push("-ignoreMinorErrors");
|
||||
}
|
||||
result.push("-execute");
|
||||
return result.join("\n") + "\n";
|
||||
}
|
||||
errors = [];
|
||||
warnings = [];
|
||||
constructor(args, options) {
|
||||
super(ExifToolTask.renderCommand(args, options), (stdout, stderr, passed) => this.#parser(stdout, stderr, passed));
|
||||
this.args = args;
|
||||
this.options = options;
|
||||
}
|
||||
onStderr(buf) {
|
||||
if (BadPerlInstallationRE.test(buf.toString())) {
|
||||
// This isn't an error we can recover from: there's a Perl module that
|
||||
// needs to be installed. See
|
||||
// https://github.com/photostructure/exiftool-vendored.js/issues/168 for
|
||||
// details.
|
||||
throw new Error(buf.toString());
|
||||
}
|
||||
super.onStderr(buf);
|
||||
}
|
||||
#parser(stdout, stderr, passed) {
|
||||
let error;
|
||||
if ((0, String_1.notBlank)(stderr) || !passed) {
|
||||
for (const line of (0, String_1.splitLines)(stderr ?? "")) {
|
||||
if ((0, IsWarning_1.isWarning)(line)) {
|
||||
this.warnings.push(line);
|
||||
}
|
||||
else if (/error|warning/i.test(line)) {
|
||||
this.errors.push(line);
|
||||
// new Error() will add a "Error: " prefix:
|
||||
error ??= new Error(line.replace(/^error: /i, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.parse(stdout, error);
|
||||
}
|
||||
}
|
||||
exports.ExifToolTask = ExifToolTask;
|
||||
//# sourceMappingURL=ExifToolTask.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifToolTask.js","sourceRoot":"","sources":["../src/ExifToolTask.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAAoC;AAEpC,2CAAwC;AAExC,qCAAuD;AAEvD,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AAI1D,MAAsB,YAAgB,SAAQ,EAAE,CAAC,IAAO;IAc3C;IACA;IAdX,MAAM,CAAC,aAAa,CAAC,IAAc,EAAE,OAA6B;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAA,cAAK,EAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,IAAI,OAAO,EAAE,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClC,CAAC;IAEQ,MAAM,GAAa,EAAE,CAAC;IACtB,QAAQ,GAAa,EAAE,CAAC;IAEjC,YACW,IAAc,EACd,OAA6B;QAEtC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAC1E,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CACrC,CAAC;QALO,SAAI,GAAJ,IAAI,CAAU;QACd,YAAO,GAAP,OAAO,CAAsB;IAKxC,CAAC;IAEQ,QAAQ,CAAC,GAAoB;QACpC,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;YAC/C,sEAAsE;YACtE,6BAA6B;YAC7B,wEAAwE;YACxE,WAAW;YACX,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,MAA0B,EAAE,MAAe;QACjE,IAAI,KAAmB,CAAC;QACxB,IAAI,IAAA,iBAAQ,EAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,KAAK,MAAM,IAAI,IAAI,IAAA,mBAAU,EAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC5C,IAAI,IAAA,qBAAS,EAAC,IAAI,CAAC,EAAE,CAAC;oBACpB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3B,CAAC;qBAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACvC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACvB,2CAA2C;oBAC3C,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;CAGF;AAlDD,oCAkDC"}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { ErrorsAndWarnings } from "./ErrorsAndWarnings";
|
||||
import { StrEnum, StrEnumKeys } from "./StrEnum";
|
||||
/**
|
||||
* This tags are added to {@link Tags} from this library.
|
||||
*/
|
||||
export interface ExifToolVendoredTags extends ErrorsAndWarnings {
|
||||
/**
|
||||
* Either an offset, like `UTC-7`, or an actual IANA timezone, like
|
||||
* `America/Los_Angeles`.
|
||||
*
|
||||
* This will be missing if we can't intuit a timezone from the metadata.
|
||||
* @deprecated use `zone` instead
|
||||
*/
|
||||
tz?: string;
|
||||
/**
|
||||
* The IANA timezone, like `America/Los_Angeles`, or a IANA-rendered static offset, like `UTC-7`.
|
||||
*
|
||||
* This will be missing if we can't intuit a timezone from the metadata.
|
||||
*/
|
||||
zone?: string;
|
||||
/**
|
||||
* Description of where and how `tz` was extracted
|
||||
* @deprecated use `zoneSource` instead
|
||||
*/
|
||||
tzSource?: string;
|
||||
/**
|
||||
* Description of where and how `zone` was extracted
|
||||
*/
|
||||
zoneSource?: string;
|
||||
}
|
||||
export declare const ExifToolVendoredTagNames: StrEnum<"zone" | "tz" | "tzSource" | "zoneSource" | "errors" | "warnings">;
|
||||
export type ExifToolVendoredTagName = StrEnumKeys<typeof ExifToolVendoredTagNames>;
|
||||
export declare function isExifToolVendoredTag(name: string): name is keyof ExifToolVendoredTags;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExifToolVendoredTagNames = void 0;
|
||||
exports.isExifToolVendoredTag = isExifToolVendoredTag;
|
||||
const StrEnum_1 = require("./StrEnum");
|
||||
exports.ExifToolVendoredTagNames = (0, StrEnum_1.strEnum)("tz", "zone", "tzSource", "zoneSource", "errors", "warnings");
|
||||
function isExifToolVendoredTag(name) {
|
||||
return exports.ExifToolVendoredTagNames.includes(name);
|
||||
}
|
||||
//# sourceMappingURL=ExifToolVendoredTags.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExifToolVendoredTags.js","sourceRoot":"","sources":["../src/ExifToolVendoredTags.ts"],"names":[],"mappings":";;;AAgDA,sDAIC;AAnDD,uCAA0D;AAkC7C,QAAA,wBAAwB,GAAG,IAAA,iBAAO,EAC7C,IAAI,EACJ,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,UAAU,CACmC,CAAC;AAMhD,SAAgB,qBAAqB,CACnC,IAAY;IAEZ,OAAO,gCAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC"}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { Logger } from "batch-cluster";
|
||||
/**
|
||||
* This implementation relies on the fact that both `exiftool-vendored.pl` and
|
||||
* `exiftool-vendored.exe` both export the path to their respective exiftool
|
||||
* binary.
|
||||
*
|
||||
* When running in node, this method should suffice.
|
||||
*
|
||||
* When running in Electron, all bets are off, due to ASAR packaging and other
|
||||
* nonsense. As perl can't run from within an ASAR archive, `electron-builder`
|
||||
* must be configured to `asarUnpack`
|
||||
* "**/node_modules/exiftool-vendored.{pl,exe}/". See
|
||||
* <https://www.electron.build/generated/platformspecificbuildoptions#configuration-asarUnpack>
|
||||
* for details.
|
||||
*
|
||||
* If you're using `electron-forge`, add something like the following to your
|
||||
* ForgeConfig.packagerConfig.extraResource array: `fs.join(".", "node_modules",
|
||||
* "exiftool-vendored." + (isWin ? "exe" : "pl"))` but then you must specify a
|
||||
* custom exiftoolPath in your options hash, as subprocesses that use
|
||||
* ELECTRON_RUN_AS_NODE will no longer have process.resourcesPath set.
|
||||
*
|
||||
* If none of the above work for your use case, you can provide your own
|
||||
* `exiftoolPath` implementation to your instance of ExifTool
|
||||
*
|
||||
* @return the path to the exiftool binary, preferring the vendored version in
|
||||
* node_modules.
|
||||
*/
|
||||
export declare function exiftoolPath(logger?: Logger): Promise<string>;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.exiftoolPath = exiftoolPath;
|
||||
const _fs = __importStar(require("node:fs"));
|
||||
const _path = __importStar(require("node:path"));
|
||||
const IsWin32_1 = require("./IsWin32");
|
||||
const Which_1 = require("./Which");
|
||||
function vendorPackage() {
|
||||
return "exiftool-vendored." + ((0, IsWin32_1.isWin32)() ? "exe" : "pl");
|
||||
}
|
||||
function tryRequire({ prefix = "", logger, } = {}) {
|
||||
const id = prefix + vendorPackage();
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
return require(id);
|
||||
}
|
||||
catch (error) {
|
||||
logger?.warn(id + "not found: ", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This implementation relies on the fact that both `exiftool-vendored.pl` and
|
||||
* `exiftool-vendored.exe` both export the path to their respective exiftool
|
||||
* binary.
|
||||
*
|
||||
* When running in node, this method should suffice.
|
||||
*
|
||||
* When running in Electron, all bets are off, due to ASAR packaging and other
|
||||
* nonsense. As perl can't run from within an ASAR archive, `electron-builder`
|
||||
* must be configured to `asarUnpack`
|
||||
* "**/node_modules/exiftool-vendored.{pl,exe}/". See
|
||||
* <https://www.electron.build/generated/platformspecificbuildoptions#configuration-asarUnpack>
|
||||
* for details.
|
||||
*
|
||||
* If you're using `electron-forge`, add something like the following to your
|
||||
* ForgeConfig.packagerConfig.extraResource array: `fs.join(".", "node_modules",
|
||||
* "exiftool-vendored." + (isWin ? "exe" : "pl"))` but then you must specify a
|
||||
* custom exiftoolPath in your options hash, as subprocesses that use
|
||||
* ELECTRON_RUN_AS_NODE will no longer have process.resourcesPath set.
|
||||
*
|
||||
* If none of the above work for your use case, you can provide your own
|
||||
* `exiftoolPath` implementation to your instance of ExifTool
|
||||
*
|
||||
* @return the path to the exiftool binary, preferring the vendored version in
|
||||
* node_modules.
|
||||
*/
|
||||
async function exiftoolPath(logger) {
|
||||
const path = tryRequire({ prefix: "", logger });
|
||||
// This s/app.asar/app.asar.unpacked/ path switch adds support for Electron
|
||||
// apps whose modules are ASAR-packed (like by electron-builder).
|
||||
const asarUnpackedPath = path
|
||||
?.split(_path.sep)
|
||||
.map((ea) => (ea === "app.asar" ? "app.asar.unpacked" : ea))
|
||||
.join(_path.sep);
|
||||
// NOTE: we must check for the fixedPath FIRST, because Electron's ASAR
|
||||
// shenanigans will make existsSync return true for asar-packed resources
|
||||
if (asarUnpackedPath != null && _fs.existsSync(asarUnpackedPath)) {
|
||||
return asarUnpackedPath;
|
||||
}
|
||||
if (path != null && _fs.existsSync(path)) {
|
||||
return path;
|
||||
}
|
||||
logger?.warn("Failed to find exiftool via " + vendorPackage());
|
||||
// process.resourcesPath is set by electron-forge:
|
||||
const electronResourcePath = process
|
||||
.resourcesPath;
|
||||
if (electronResourcePath != null) {
|
||||
const forgePath = _path.join(electronResourcePath, vendorPackage(), "bin", "exiftool" + ((0, IsWin32_1.isWin32)() ? ".exe" : ""));
|
||||
if (_fs.existsSync(forgePath)) {
|
||||
return forgePath;
|
||||
}
|
||||
else {
|
||||
logger?.warn("Failed to find exiftool in electron forge resources path: " +
|
||||
forgePath);
|
||||
}
|
||||
}
|
||||
// Last ditch: is there a globally installed exiftool in the PATH?
|
||||
const fromPath = await (0, Which_1.which)("exiftool");
|
||||
if (fromPath != null) {
|
||||
return fromPath;
|
||||
}
|
||||
throw new Error(`Failed to find ExifTool installation: set exiftoolPath explicitly.`);
|
||||
}
|
||||
//# sourceMappingURL=ExiftoolPath.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExiftoolPath.js","sourceRoot":"","sources":["../src/ExiftoolPath.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,oCAkDC;AApGD,6CAA+B;AAC/B,iDAAmC;AACnC,uCAAoC;AAEpC,mCAAgC;AAEhC,SAAS,aAAa;IACpB,OAAO,oBAAoB,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,MAAM,GAAG,EAAE,EACX,MAAM,MACyC,EAAE;IACjD,MAAM,EAAE,GAAG,MAAM,GAAG,aAAa,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,iEAAiE;QACjE,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,aAAa,EAAE,KAAK,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,KAAK,UAAU,YAAY,CAAC,MAAe;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAChD,2EAA2E;IAC3E,iEAAiE;IAEjE,MAAM,gBAAgB,GAAG,IAAI;QAC3B,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;SACjB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC3D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEnB,uEAAuE;IACvE,yEAAyE;IACzE,IAAI,gBAAgB,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,IAAI,CAAC,8BAA8B,GAAG,aAAa,EAAE,CAAC,CAAC;IAE/D,kDAAkD;IAClD,MAAM,oBAAoB,GAAI,OAAsC;SACjE,aAAa,CAAC;IACjB,IAAI,oBAAoB,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,oBAAoB,EACpB,aAAa,EAAE,EACf,KAAK,EACL,UAAU,GAAG,CAAC,IAAA,iBAAO,GAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CACvC,CAAC;QACF,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,IAAI,CACV,4DAA4D;gBAC1D,SAAS,CACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,IAAA,aAAK,EAAC,UAAU,CAAC,CAAC;IACzC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;AACJ,CAAC"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export declare function isFileEmpty(path: string): Promise<boolean>;
|
||||
export declare const isPlatformCaseSensitive: () => boolean;
|
||||
export declare function compareFilePaths(a: string, b: string): boolean;
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isPlatformCaseSensitive = void 0;
|
||||
exports.isFileEmpty = isFileEmpty;
|
||||
exports.compareFilePaths = compareFilePaths;
|
||||
const node_fs_1 = require("node:fs");
|
||||
const node_path_1 = require("node:path");
|
||||
const Lazy_1 = require("./Lazy");
|
||||
const String_1 = require("./String");
|
||||
async function isFileEmpty(path) {
|
||||
if ((0, String_1.blank)(path)) {
|
||||
throw new Error("isFileEmpty(): blank path");
|
||||
}
|
||||
// TODO: convert this to using fs/promises once node 12 is EOL (2022-04-30)
|
||||
try {
|
||||
const s = await new Promise((res, rej) => {
|
||||
try {
|
||||
(0, node_fs_1.stat)(path, (err, val) => (err == null ? res(val) : rej(err)));
|
||||
}
|
||||
catch (err) {
|
||||
rej(err);
|
||||
}
|
||||
});
|
||||
return s == null || s.size === 0;
|
||||
}
|
||||
catch (err) {
|
||||
if (err &&
|
||||
typeof err === "object" &&
|
||||
"code" in err &&
|
||||
err.code === "ENOENT")
|
||||
return true;
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
exports.isPlatformCaseSensitive = (0, Lazy_1.lazy)(() => process.platform !== "win32" && process.platform !== "darwin");
|
||||
function compareFilePaths(a, b) {
|
||||
const aNorm = (0, node_path_1.normalize)(a);
|
||||
const bNorm = (0, node_path_1.normalize)(b);
|
||||
return (0, exports.isPlatformCaseSensitive)()
|
||||
? aNorm === bNorm
|
||||
: aNorm.localeCompare(bNorm, undefined, { sensitivity: "base" }) === 0;
|
||||
}
|
||||
//# sourceMappingURL=File.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"File.js","sourceRoot":"","sources":["../src/File.ts"],"names":[],"mappings":";;;AAKA,kCAyBC;AAMD,4CAMC;AA1CD,qCAAsC;AACtC,yCAAsC;AACtC,iCAA8B;AAC9B,qCAAiC;AAE1B,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,IAAA,cAAK,EAAC,IAAI,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,2EAA2E;IAC3E,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,CAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC9C,IAAI,CAAC;gBACH,IAAA,cAAI,EAAC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,GAAG,CAAC,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IACE,GAAG;YACH,OAAO,GAAG,KAAK,QAAQ;YACvB,MAAM,IAAI,GAAG;YACb,GAAG,CAAC,IAAI,KAAK,QAAQ;YAErB,OAAO,IAAI,CAAC;;YACT,MAAM,GAAG,CAAC;IACjB,CAAC;AACH,CAAC;AAEY,QAAA,uBAAuB,GAAG,IAAA,WAAI,EACzC,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,CACpE,CAAC;AAEF,SAAgB,gBAAgB,CAAC,CAAS,EAAE,CAAS;IACnD,MAAM,KAAK,GAAG,IAAA,qBAAS,EAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAA,qBAAS,EAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,IAAA,+BAAuB,GAAE;QAC9B,CAAC,CAAC,KAAK,KAAK,KAAK;QACjB,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC"}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { FileTags } from "./Tags";
|
||||
/**
|
||||
* Is the given tag name intrinsic to the content of a given file?
|
||||
*
|
||||
* In other words, is it not an artifact of a metadata field?
|
||||
*/
|
||||
export declare function isFileTag(name: string): name is keyof FileTags;
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isFileTag = isFileTag;
|
||||
const Object_1 = require("./Object");
|
||||
const FileTagNames = (0, Object_1.keysOf)({
|
||||
BMPVersion: true,
|
||||
BitsPerSample: true,
|
||||
ColorComponents: true,
|
||||
CurrentIPTCDigest: true,
|
||||
Directory: true,
|
||||
EncodingProcess: true,
|
||||
ExifByteOrder: true,
|
||||
FileAccessDate: true,
|
||||
FileCreateDate: true,
|
||||
FileInodeChangeDate: true,
|
||||
FileModifyDate: true,
|
||||
FileName: true,
|
||||
FilePermissions: true,
|
||||
FileSize: true,
|
||||
FileType: true,
|
||||
FileTypeExtension: true,
|
||||
ImageDataMD5: true,
|
||||
ImageHeight: true,
|
||||
ImageWidth: true,
|
||||
MIMEType: true,
|
||||
NumColors: true,
|
||||
NumImportantColors: true,
|
||||
PixelsPerMeterX: true,
|
||||
PixelsPerMeterY: true,
|
||||
Planes: true,
|
||||
YCbCrSubSampling: true,
|
||||
});
|
||||
/**
|
||||
* Is the given tag name intrinsic to the content of a given file?
|
||||
*
|
||||
* In other words, is it not an artifact of a metadata field?
|
||||
*/
|
||||
function isFileTag(name) {
|
||||
return FileTagNames.includes(name);
|
||||
}
|
||||
//# sourceMappingURL=FileTags.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"FileTags.js","sourceRoot":"","sources":["../src/FileTags.ts"],"names":[],"mappings":";;AAqCA,8BAEC;AAvCD,qCAAkC;AAGlC,MAAM,YAAY,GAAG,IAAA,eAAM,EAAW;IACpC,UAAU,EAAE,IAAI;IAChB,aAAa,EAAE,IAAI;IACnB,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,IAAI;IACvB,SAAS,EAAE,IAAI;IACf,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,IAAI;IACpB,cAAc,EAAE,IAAI;IACpB,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,QAAQ,EAAE,IAAI;IACd,eAAe,EAAE,IAAI;IACrB,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,iBAAiB,EAAE,IAAI;IACvB,YAAY,EAAE,IAAI;IAClB,WAAW,EAAE,IAAI;IACjB,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,IAAI;IACf,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,IAAI;IACrB,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,IAAI;CACvB,CAAC,CAAC;AAEH;;;;GAIG;AACH,SAAgB,SAAS,CAAC,IAAY;IACpC,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAsB,CAAC,CAAC;AACvD,CAAC"}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Tells ExifTool to handle UTF-8-encoded filenames on Windows.
|
||||
*
|
||||
* Without this setting, non-latin filename characters on Windows won't be
|
||||
* encoded correctly, and the file will be erroneously reported as not
|
||||
* existing.
|
||||
*
|
||||
* This setting isn't necessary on non-Windows platforms, nor on Windows
|
||||
* machines that have been configured to use UTF-8, but it doesn't hurt
|
||||
* anything to include this option.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/124
|
||||
* @see https://exiftool.org/exiftool_pod.html#WINDOWS-UNICODE-FILE-NAMES
|
||||
* @see https://exiftool.org/faq.html#Q10
|
||||
*/
|
||||
export declare const Utf8FilenameCharsetArgs: string[];
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Utf8FilenameCharsetArgs = void 0;
|
||||
const IsWin32_1 = require("./IsWin32");
|
||||
/**
|
||||
* Tells ExifTool to handle UTF-8-encoded filenames on Windows.
|
||||
*
|
||||
* Without this setting, non-latin filename characters on Windows won't be
|
||||
* encoded correctly, and the file will be erroneously reported as not
|
||||
* existing.
|
||||
*
|
||||
* This setting isn't necessary on non-Windows platforms, nor on Windows
|
||||
* machines that have been configured to use UTF-8, but it doesn't hurt
|
||||
* anything to include this option.
|
||||
*
|
||||
* @see https://github.com/photostructure/exiftool-vendored.js/issues/124
|
||||
* @see https://exiftool.org/exiftool_pod.html#WINDOWS-UNICODE-FILE-NAMES
|
||||
* @see https://exiftool.org/faq.html#Q10
|
||||
*/
|
||||
exports.Utf8FilenameCharsetArgs = (0, IsWin32_1.isWin32)()
|
||||
? ["-charset", "filename=utf8"]
|
||||
: [];
|
||||
//# sourceMappingURL=FilenameCharsetArgs.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"FilenameCharsetArgs.js","sourceRoot":"","sources":["../src/FilenameCharsetArgs.ts"],"names":[],"mappings":";;;AAAA,uCAAoC;AAEpC;;;;;;;;;;;;;;GAcG;AACU,QAAA,uBAAuB,GAAG,IAAA,iBAAO,GAAE;IAC9C,CAAC,CAAC,CAAC,UAAU,EAAE,eAAe,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC"}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { ExifDateTime } from "./ExifDateTime";
|
||||
import { Maybe } from "./Maybe";
|
||||
import { MaybeReadonly } from "./MaybeReadonly";
|
||||
import { Tags } from "./Tags";
|
||||
/**
|
||||
* Returns the first date/time tag in `dateTimeTags` that strictly parses from
|
||||
* EXIF.
|
||||
*/
|
||||
export declare function firstDateTime(tags: Maybe<Tags>, dateTimeTags?: MaybeReadonly<(keyof Tags)[]>): Maybe<ExifDateTime>;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.firstDateTime = firstDateTime;
|
||||
const CapturedAtTagNames_1 = require("./CapturedAtTagNames");
|
||||
const ExifDateTime_1 = require("./ExifDateTime");
|
||||
const String_1 = require("./String");
|
||||
/**
|
||||
* Returns the first date/time tag in `dateTimeTags` that strictly parses from
|
||||
* EXIF.
|
||||
*/
|
||||
function firstDateTime(tags, dateTimeTags = CapturedAtTagNames_1.CapturedAtTagNames) {
|
||||
for (const tag of dateTimeTags) {
|
||||
const dt = tags?.[tag];
|
||||
if (dt instanceof ExifDateTime_1.ExifDateTime) {
|
||||
return dt;
|
||||
}
|
||||
else if ((0, String_1.isString)(dt)) {
|
||||
const edt = ExifDateTime_1.ExifDateTime.fromEXIF(dt);
|
||||
if (edt != null) {
|
||||
return edt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
//# sourceMappingURL=FirstDateTime.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"FirstDateTime.js","sourceRoot":"","sources":["../src/FirstDateTime.ts"],"names":[],"mappings":";;AAWA,sCAgBC;AA3BD,6DAA0D;AAC1D,iDAA8C;AAG9C,qCAAoC;AAGpC;;;GAGG;AACH,SAAgB,aAAa,CAC3B,IAAiB,EACjB,eAA8C,uCAAkB;IAEhE,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,EAAE,YAAY,2BAAY,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;QACZ,CAAC;aAAM,IAAI,IAAA,iBAAQ,EAAC,EAAE,CAAC,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,2BAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC;YACb,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO;AACT,CAAC"}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { ExifToolOptions } from "./ExifToolOptions";
|
||||
import { Maybe } from "./Maybe";
|
||||
export type GpsLocationTags = {
|
||||
GPSLatitude?: number;
|
||||
GPSLatitudeRef?: string;
|
||||
GPSLongitude?: number;
|
||||
GPSLongitudeRef?: string;
|
||||
GPSPosition?: string;
|
||||
GeolocationPosition?: string;
|
||||
};
|
||||
export declare const GpsLocationTagNames: (keyof GpsLocationTags)[];
|
||||
export interface GpsParseResult {
|
||||
result: GpsLocationTags;
|
||||
details: string;
|
||||
invalid: boolean;
|
||||
warnings: string[];
|
||||
}
|
||||
export declare function parseGPSLocation(tags: GpsLocationTags, opts: Pick<ExifToolOptions, "ignoreZeroZeroLatLon">): Maybe<Partial<GpsParseResult>>;
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GpsLocationTagNames = void 0;
|
||||
exports.parseGPSLocation = parseGPSLocation;
|
||||
const CoordinateParser_1 = require("./CoordinateParser");
|
||||
const Lazy_1 = require("./Lazy");
|
||||
const Number_1 = require("./Number");
|
||||
const Object_1 = require("./Object");
|
||||
const String_1 = require("./String");
|
||||
exports.GpsLocationTagNames = (0, Object_1.keysOf)({
|
||||
GPSLatitude: true,
|
||||
GPSLatitudeRef: true,
|
||||
GPSLongitude: true,
|
||||
GPSLongitudeRef: true,
|
||||
GPSPosition: true,
|
||||
GeolocationPosition: true,
|
||||
});
|
||||
// local function that handles more input types:
|
||||
function _parseCoordinate(v) {
|
||||
return (0, String_1.blank)(v) ? undefined : (0, Number_1.isNumber)(v) ? v : (0, CoordinateParser_1.parseCoordinate)(v).decimal;
|
||||
}
|
||||
function _parseCoordinates(v) {
|
||||
return (0, String_1.blank)(v) ? undefined : (0, CoordinateParser_1.parseCoordinates)(v);
|
||||
}
|
||||
function parseGPSLocation(tags, opts) {
|
||||
const warnings = [];
|
||||
try {
|
||||
// Parse primary coordinates with error capturing
|
||||
let latitude = undefined;
|
||||
let longitude = undefined;
|
||||
try {
|
||||
latitude = _parseCoordinate(tags.GPSLatitude);
|
||||
}
|
||||
catch (e) {
|
||||
warnings.push(`Error parsing GPSLatitude: ${e}`);
|
||||
}
|
||||
try {
|
||||
longitude = _parseCoordinate(tags.GPSLongitude);
|
||||
}
|
||||
catch (e) {
|
||||
warnings.push(`Error parsing GPSLongitude: ${e}`);
|
||||
}
|
||||
// If either coordinate is missing, try GPSPosition
|
||||
if (latitude == null || longitude == null) {
|
||||
const gpsPos = (0, Lazy_1.lazy)(() => {
|
||||
try {
|
||||
return _parseCoordinates(tags.GPSPosition);
|
||||
}
|
||||
catch (e) {
|
||||
warnings.push(`Error parsing GPSPosition: ${e}`);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
if (latitude == null) {
|
||||
latitude = gpsPos()?.latitude;
|
||||
}
|
||||
if (longitude == null) {
|
||||
longitude = gpsPos()?.longitude;
|
||||
}
|
||||
}
|
||||
// If we still don't have both coordinates, return early
|
||||
if (latitude == null || longitude == null) {
|
||||
return { invalid: false, warnings };
|
||||
}
|
||||
// Check for zero coordinates if configured
|
||||
if (opts.ignoreZeroZeroLatLon && latitude === 0 && longitude === 0) {
|
||||
warnings.push("Ignoring zero coordinates from GPSLatitude/GPSLongitude");
|
||||
return { invalid: true, warnings };
|
||||
}
|
||||
// Get geolocation reference values for sign validation
|
||||
let geoPos = undefined;
|
||||
try {
|
||||
geoPos = _parseCoordinates(tags.GeolocationPosition);
|
||||
}
|
||||
catch (e) {
|
||||
warnings.push(`Error parsing GeolocationPosition: ${e}`);
|
||||
}
|
||||
// Process coordinates with validation and sign correction
|
||||
const latResult = (0, CoordinateParser_1.processCoordinate)({
|
||||
value: latitude,
|
||||
ref: tags.GPSLatitudeRef,
|
||||
geoValue: geoPos?.latitude,
|
||||
expectedRefPositive: "N",
|
||||
expectedRefNegative: "S",
|
||||
max: 90,
|
||||
coordinateType: "Latitude",
|
||||
}, warnings);
|
||||
const lonResult = (0, CoordinateParser_1.processCoordinate)({
|
||||
value: longitude,
|
||||
ref: tags.GPSLongitudeRef,
|
||||
geoValue: geoPos?.longitude,
|
||||
expectedRefPositive: "E",
|
||||
expectedRefNegative: "W",
|
||||
max: 180,
|
||||
coordinateType: "Longitude",
|
||||
}, warnings);
|
||||
if (latResult.isInvalid || lonResult.isInvalid) {
|
||||
return { invalid: true, warnings };
|
||||
}
|
||||
return {
|
||||
result: {
|
||||
GPSLatitude: latResult.value,
|
||||
GPSLongitude: lonResult.value,
|
||||
GPSLatitudeRef: latResult.ref,
|
||||
GPSLongitudeRef: lonResult.ref,
|
||||
},
|
||||
invalid: false,
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
warnings.push(`Error parsing coordinates: ${e}`);
|
||||
return { invalid: true, warnings };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=GPS.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GPS.js","sourceRoot":"","sources":["../src/GPS.ts"],"names":[],"mappings":"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"}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import { Maybe } from "./Maybe";
|
||||
/**
|
||||
* @throws if the given latitude and longitude are invalid.
|
||||
*/
|
||||
export declare function geoTz(lat: number, lon: number): Maybe<string>;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.geoTz = geoTz;
|
||||
const tz_lookup_1 = __importDefault(require("@photostructure/tz-lookup"));
|
||||
/**
|
||||
* @throws if the given latitude and longitude are invalid.
|
||||
*/
|
||||
function geoTz(lat, lon) {
|
||||
return (0, tz_lookup_1.default)(lat, lon);
|
||||
}
|
||||
//# sourceMappingURL=GeoTz.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GeoTz.js","sourceRoot":"","sources":["../src/GeoTz.ts"],"names":[],"mappings":";;;;;AAMA,sBAEC;AARD,0EAAkD;AAGlD;;GAEG;AACH,SAAgB,KAAK,CAAC,GAAW,EAAE,GAAW;IAC5C,OAAO,IAAA,mBAAS,EAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { StrEnum, StrEnumKeys } from "./StrEnum";
|
||||
/**
|
||||
* These tags are only available if {@link ExifToolOptions.geolocation} is true
|
||||
* and the file has valid GPS location data.
|
||||
*
|
||||
* @see https://exiftool.org/geolocation.html#Read
|
||||
*/
|
||||
export interface GeolocationTags {
|
||||
/** ☆☆☆☆ ✔ Example: 99 */
|
||||
GeolocationBearing?: number;
|
||||
/** ☆☆☆☆ ✔ Example: "Zürich" */
|
||||
GeolocationCity?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "United States" */
|
||||
GeolocationCountry?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "US" */
|
||||
GeolocationCountryCode?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "9.60 km" */
|
||||
GeolocationDistance?: string;
|
||||
/**
|
||||
* ☆☆☆☆ ✔ Example: "PPLL"
|
||||
* @see http://www.geonames.org/export/codes.html#P
|
||||
*/
|
||||
GeolocationFeatureCode?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "Populated Place" */
|
||||
GeolocationFeatureType?: string;
|
||||
/** ☆☆☆☆ ✔ Example: 95000 */
|
||||
GeolocationPopulation?: number;
|
||||
/** ☆☆☆☆ ✔ Example: "7.3397, 134.4733" */
|
||||
GeolocationPosition?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "Île-de-France" */
|
||||
GeolocationRegion?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "Yokohama Shi" */
|
||||
GeolocationSubregion?: string;
|
||||
/** ☆☆☆☆ ✔ Example: "Pacific/Saipan"
|
||||
*
|
||||
* IANA time zone name
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
*/
|
||||
GeolocationTimeZone?: string;
|
||||
GeolocationWarning?: string;
|
||||
}
|
||||
export declare const GeolocationTagNames: StrEnum<"GeolocationBearing" | "GeolocationCity" | "GeolocationCountry" | "GeolocationCountryCode" | "GeolocationDistance" | "GeolocationFeatureCode" | "GeolocationFeatureType" | "GeolocationPopulation" | "GeolocationPosition" | "GeolocationRegion" | "GeolocationSubregion" | "GeolocationTimeZone" | "GeolocationWarning">;
|
||||
export type GeolocationTagName = StrEnumKeys<typeof GeolocationTagNames>;
|
||||
export declare function isGeolocationTag(name: string): name is keyof GeolocationTags;
|
||||
Loaded 100 of 5445 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user